From 5c3da177e2e56ac9fe28bb72ae5acd8aeca902c3 Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Sat, 27 Nov 2021 15:07:16 +0900 Subject: [PATCH 1/5] Adding support for Yarp native configuration --- .../YarpJsonFileConfigProvider.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs diff --git a/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs b/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs new file mode 100644 index 0000000..89382a0 --- /dev/null +++ b/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class YarpJsonFileConfigProvider : IProxyConfigProvider + { + public YarpJsonFileConfigProvider() + { + _fileProvider = new PhysicalFileProvider(_wwwroot) + { + UseActivePolling = true, + UsePollingFileWatcher = true + }; + } + + private readonly PhysicalFileProvider _fileProvider; + + private const string YarpJsonFileName = "yarp.json"; + + private static readonly string _wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); + + public IProxyConfig GetConfig() + { + var yarpJsonFile = Path.Combine(_wwwroot, YarpJsonFileName); + + var changeToken = _fileProvider.Watch(yarpJsonFile); + + return new YarpJsonFileConfig(yarpJsonFile, changeToken); + } + + private class YarpJsonFileConfig : IProxyConfig + { + public YarpJsonFileConfig(string yarpJsonFile, IChangeToken changeToken) + { + try + { + var json = File.ReadAllText(yarpJsonFile); + } + catch + { + Routes = Array.Empty(); + Clusters = Array.Empty(); + } + + ChangeToken = changeToken; + } + + public IReadOnlyList Routes { get; } + + public IReadOnlyList Clusters { get; } + + public IChangeToken ChangeToken { get; } + } + } +} From 905cfc0a23ba73d8def11749b670ceb4eb9a08c5 Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Sat, 27 Nov 2021 15:15:13 +0900 Subject: [PATCH 2/5] Adding YARP native json loader --- AppServiceProxy/Configuration/YarpJson.cs | 11 +++++++++++ .../Configuration/YarpJsonFileConfigProvider.cs | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 AppServiceProxy/Configuration/YarpJson.cs diff --git a/AppServiceProxy/Configuration/YarpJson.cs b/AppServiceProxy/Configuration/YarpJson.cs new file mode 100644 index 0000000..b1b6af3 --- /dev/null +++ b/AppServiceProxy/Configuration/YarpJson.cs @@ -0,0 +1,11 @@ +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class YarpJson + { + public IReadOnlyList Routes { get; set; } = null!; + + public IReadOnlyList Clusters { get; set; } = null!; + } +} diff --git a/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs b/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs index 89382a0..e8b1f1e 100644 --- a/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs +++ b/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.FileProviders; +using System.Text.Json; + +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Yarp.ReverseProxy.Configuration; @@ -38,6 +40,11 @@ public YarpJsonFileConfig(string yarpJsonFile, IChangeToken changeToken) try { var json = File.ReadAllText(yarpJsonFile); + + var yarp = JsonSerializer.Deserialize(json); + + Routes = yarp?.Routes ?? Array.Empty(); + Clusters = yarp?.Clusters ?? Array.Empty(); ; } catch { From 1259d304c10a6e3fdc386b4baf404f2b11b9f8aa Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Sun, 23 Jan 2022 19:21:22 +0900 Subject: [PATCH 3/5] Adding file-based proxy config provider --- .../FileBaseProxyConfigProvider.cs | 63 ++++++++++++++++++ .../Configuration/IConfigFileLoader.cs | 10 +++ .../ProxiesJsonConfigFileLoader.cs | 24 +++++++ .../ProxiesJsonFileConfigProvider.cs | 65 ------------------- .../Configuration/ProxiesJsonReader.cs | 6 +- .../Configuration/YarpJsonConfigFileLoader.cs | 25 +++++++ .../YarpJsonFileConfigProvider.cs | 65 ------------------- AppServiceProxy/Program.cs | 7 +- 8 files changed, 131 insertions(+), 134 deletions(-) create mode 100644 AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs create mode 100644 AppServiceProxy/Configuration/IConfigFileLoader.cs create mode 100644 AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs delete mode 100644 AppServiceProxy/Configuration/ProxiesJsonFileConfigProvider.cs create mode 100644 AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs delete mode 100644 AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs diff --git a/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs new file mode 100644 index 0000000..1eab311 --- /dev/null +++ b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs @@ -0,0 +1,63 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class FileBaseProxyConfigProvider : IProxyConfigProvider + { + public FileBaseProxyConfigProvider(IEnumerable configFileLoaders) + { + _configFileLoaders = configFileLoaders; + + _fileProvider = new PhysicalFileProvider(_wwwroot) + { + UseActivePolling = true, + UsePollingFileWatcher = true + }; + } + + private readonly IEnumerable _configFileLoaders; + private readonly PhysicalFileProvider _fileProvider; + + private static readonly string _wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); + + public IProxyConfig GetConfig() + { + var configFileLoader = _configFileLoaders.FirstOrDefault(x => File.Exists(Path.Combine(_wwwroot, x.ConfigFileName))); + + if (configFileLoader is null) + { + return new FileBaseProxyConfig + { + Routes = Array.Empty(), + Clusters = Array.Empty(), + ChangeToken = NullChangeToken.Singleton + }; + } + + var contents = File.ReadAllText(Path.Combine(_wwwroot, configFileLoader.ConfigFileName)); + + var (routes, clusters) = configFileLoader.LoadConfig(contents); + + var changeToken = _fileProvider.Watch(configFileLoader.ConfigFileName); + + return new FileBaseProxyConfig + { + Routes = routes, + Clusters = clusters, + ChangeToken = changeToken + }; + } + + private class FileBaseProxyConfig : IProxyConfig + { + public IReadOnlyList Routes { get; init; } = null!; + + public IReadOnlyList Clusters { get; init; } = null!; + + public IChangeToken ChangeToken { get; init; } = null!; + } + } +} diff --git a/AppServiceProxy/Configuration/IConfigFileLoader.cs b/AppServiceProxy/Configuration/IConfigFileLoader.cs new file mode 100644 index 0000000..b4f1f75 --- /dev/null +++ b/AppServiceProxy/Configuration/IConfigFileLoader.cs @@ -0,0 +1,10 @@ +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration; + +internal interface IConfigFileLoader +{ + string ConfigFileName { get; } + + (IReadOnlyList, IReadOnlyList) LoadConfig(string contents); +} \ No newline at end of file diff --git a/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs new file mode 100644 index 0000000..2d21fb0 --- /dev/null +++ b/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs @@ -0,0 +1,24 @@ +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class ProxiesJsonConfigFileLoader : IConfigFileLoader + { + public string ConfigFileName => "proxies.json"; + + public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) + { + try + { + var proxies = ProxiesJsonReader.ParseJson(contents); + var (routes, clusters) = ProxiesJsonTransform.Apply(proxies); + + return (routes, clusters); + } + catch + { + return (Array.Empty(), Array.Empty()); + } + } + } +} diff --git a/AppServiceProxy/Configuration/ProxiesJsonFileConfigProvider.cs b/AppServiceProxy/Configuration/ProxiesJsonFileConfigProvider.cs deleted file mode 100644 index 421d244..0000000 --- a/AppServiceProxy/Configuration/ProxiesJsonFileConfigProvider.cs +++ /dev/null @@ -1,65 +0,0 @@ - -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Primitives; - -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class ProxiesJsonFileConfigProvider : IProxyConfigProvider - { - public ProxiesJsonFileConfigProvider() - { - _fileProvider = new PhysicalFileProvider(_wwwroot) - { - UseActivePolling = true, - UsePollingFileWatcher = true - }; - } - - private readonly PhysicalFileProvider _fileProvider; - - private const string ProxiesJsonFileName = "proxies.json"; - - private static readonly string _wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); - - public IProxyConfig GetConfig() - { - var proxiesJsonFile = Path.Combine(_wwwroot, ProxiesJsonFileName); - - var changeToken = _fileProvider.Watch(ProxiesJsonFileName); - - return new ProxiesJsonFileConfig(proxiesJsonFile, changeToken); - } - - private class ProxiesJsonFileConfig : IProxyConfig - { - public ProxiesJsonFileConfig(string proxiesJsonFile, IChangeToken changeToken) - { - try - { - var json = File.ReadAllText(proxiesJsonFile); - - var proxies = ProxiesJsonReader.ParseJson(json); - var (routes, clusters) = ProxiesJsonTransform.Apply(proxies); - - Routes = routes; - Clusters = clusters; - } - catch - { - Routes = Array.Empty(); - Clusters = Array.Empty(); - } - - ChangeToken = changeToken; - } - - public IReadOnlyList Routes { get; } - - public IReadOnlyList Clusters { get; } - - public IChangeToken ChangeToken { get; } - } - } -} diff --git a/AppServiceProxy/Configuration/ProxiesJsonReader.cs b/AppServiceProxy/Configuration/ProxiesJsonReader.cs index 6e6a985..6386391 100644 --- a/AppServiceProxy/Configuration/ProxiesJsonReader.cs +++ b/AppServiceProxy/Configuration/ProxiesJsonReader.cs @@ -57,8 +57,8 @@ private static ProxyConfig ParseProxyConfig(JsonProperty proxy) var requestOverridesConfig = new RequestOverridesConfig { Method = requestOverrides.TryGetProperty("method", out var method) ? method.GetString() : null, - QueryString = requestOverrides.EnumerateObject().Where(x => x.Name.StartsWith("backend.request.querystring.")).ToDictionary(x => x.Name.Substring(28), x => x.Value.GetString()!), - Headers = requestOverrides.EnumerateObject().Where(x => x.Name.StartsWith("backend.request.headers.")).ToDictionary(x => x.Name.Substring(24), x => x.Value.GetString()!) + QueryString = requestOverrides.EnumerateObject().Where(x => x.Name.StartsWith("backend.request.querystring.")).ToDictionary(x => x.Name[28..], x => x.Value.GetString()!), + Headers = requestOverrides.EnumerateObject().Where(x => x.Name.StartsWith("backend.request.headers.")).ToDictionary(x => x.Name[24..], x => x.Value.GetString()!) }; return requestOverridesConfig; @@ -75,7 +75,7 @@ private static ProxyConfig ParseProxyConfig(JsonProperty proxy) { StatusCode = responseOverrides.TryGetProperty("response.statusCode", out var statusCode) ? statusCode.GetInt32() : null, StatusReason = responseOverrides.TryGetProperty("response.statusReason", out var statusReason) ? statusReason.GetString() : null, - Headers = responseOverrides.EnumerateObject().Where(x => x.Name.StartsWith("response.headers.")).ToDictionary(x => x.Name.Substring(17), x => x.Value.GetString()!) + Headers = responseOverrides.EnumerateObject().Where(x => x.Name.StartsWith("response.headers.")).ToDictionary(x => x.Name[17..], x => x.Value.GetString()!) }; return responseOverridesConfig; diff --git a/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs new file mode 100644 index 0000000..5483119 --- /dev/null +++ b/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs @@ -0,0 +1,25 @@ +using System.Text.Json; + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class YarpJsonConfigFileLoader : IConfigFileLoader + { + public string ConfigFileName => "yarp.json"; + + public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) + { + try + { + var yarp = JsonSerializer.Deserialize(contents); + + return (yarp?.Routes ?? Array.Empty(), yarp?.Clusters ?? Array.Empty()); + } + catch + { + return (Array.Empty(), Array.Empty()); + } + } + } +} diff --git a/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs b/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs deleted file mode 100644 index e8b1f1e..0000000 --- a/AppServiceProxy/Configuration/YarpJsonFileConfigProvider.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Text.Json; - -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Primitives; - -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class YarpJsonFileConfigProvider : IProxyConfigProvider - { - public YarpJsonFileConfigProvider() - { - _fileProvider = new PhysicalFileProvider(_wwwroot) - { - UseActivePolling = true, - UsePollingFileWatcher = true - }; - } - - private readonly PhysicalFileProvider _fileProvider; - - private const string YarpJsonFileName = "yarp.json"; - - private static readonly string _wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); - - public IProxyConfig GetConfig() - { - var yarpJsonFile = Path.Combine(_wwwroot, YarpJsonFileName); - - var changeToken = _fileProvider.Watch(yarpJsonFile); - - return new YarpJsonFileConfig(yarpJsonFile, changeToken); - } - - private class YarpJsonFileConfig : IProxyConfig - { - public YarpJsonFileConfig(string yarpJsonFile, IChangeToken changeToken) - { - try - { - var json = File.ReadAllText(yarpJsonFile); - - var yarp = JsonSerializer.Deserialize(json); - - Routes = yarp?.Routes ?? Array.Empty(); - Clusters = yarp?.Clusters ?? Array.Empty(); ; - } - catch - { - Routes = Array.Empty(); - Clusters = Array.Empty(); - } - - ChangeToken = changeToken; - } - - public IReadOnlyList Routes { get; } - - public IReadOnlyList Clusters { get; } - - public IChangeToken ChangeToken { get; } - } - } -} diff --git a/AppServiceProxy/Program.cs b/AppServiceProxy/Program.cs index 24ab44c..d22f43a 100644 --- a/AppServiceProxy/Program.cs +++ b/AppServiceProxy/Program.cs @@ -1,12 +1,17 @@ using AppServiceProxy.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; + using Yarp.ReverseProxy.Configuration; var builder = WebApplication.CreateBuilder(args); builder.Services.AddReverseProxy(); -builder.Services.AddSingleton(); +builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); +builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + +builder.Services.AddSingleton(); var app = builder.Build(); From 83a41a4da778f5357b56ef9b648cf862ac1087dc Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Sun, 23 Jan 2022 20:27:21 +0900 Subject: [PATCH 4/5] Adding yarp.json parser --- AppServiceProxy.Tests/YarpJsonReaderTests.cs | 40 ++++++++++++++ .../FileBaseProxyConfigProvider.cs | 28 ++++++---- .../Configuration/YarpJsonConfigFileLoader.cs | 8 +-- .../Configuration/YarpJsonReader.cs | 55 +++++++++++++++++++ README.md | 32 ++++++++++- 5 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 AppServiceProxy.Tests/YarpJsonReaderTests.cs create mode 100644 AppServiceProxy/Configuration/YarpJsonReader.cs diff --git a/AppServiceProxy.Tests/YarpJsonReaderTests.cs b/AppServiceProxy.Tests/YarpJsonReaderTests.cs new file mode 100644 index 0000000..8251e2c --- /dev/null +++ b/AppServiceProxy.Tests/YarpJsonReaderTests.cs @@ -0,0 +1,40 @@ +using AppServiceProxy.Configuration; + +using Xunit; + +namespace AppServiceProxy.Tests +{ + public class YarpJsonReaderTests + { + [Fact] + public void Basic() + { + var json = @" +{ + ""Routes"": { + ""route1"": { + ""ClusterId"": ""cluster1"", + ""Match"": { + ""Path"": ""{**catch-all}"" + } + } + }, + ""Clusters"": { + ""cluster1"": { + ""Destinations"": { + ""cluster1/destination1"": { + ""Address"": ""https://shibayan.jp/"" + } + } + } + } +} +"; + + var yarp = YarpJsonReader.ParseJson(json); + + Assert.Equal(1, yarp.Routes.Count); + Assert.Equal(1, yarp.Clusters.Count); + } + } +} diff --git a/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs index 1eab311..2e456bd 100644 --- a/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs +++ b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs @@ -11,6 +11,8 @@ public FileBaseProxyConfigProvider(IEnumerable configFileLoad { _configFileLoaders = configFileLoaders; + var json = System.Text.Json.JsonSerializer.Deserialize(File.ReadAllText(@"C:\Users\shibayan\Documents\yarp.json")); + _fileProvider = new PhysicalFileProvider(_wwwroot) { UseActivePolling = true, @@ -29,26 +31,30 @@ public IProxyConfig GetConfig() if (configFileLoader is null) { + var changeToken = _fileProvider.Watch("*.*"); + return new FileBaseProxyConfig { Routes = Array.Empty(), Clusters = Array.Empty(), - ChangeToken = NullChangeToken.Singleton + ChangeToken = changeToken }; } + else + { + var contents = File.ReadAllText(Path.Combine(_wwwroot, configFileLoader.ConfigFileName)); - var contents = File.ReadAllText(Path.Combine(_wwwroot, configFileLoader.ConfigFileName)); - - var (routes, clusters) = configFileLoader.LoadConfig(contents); + var (routes, clusters) = configFileLoader.LoadConfig(contents); - var changeToken = _fileProvider.Watch(configFileLoader.ConfigFileName); + var changeToken = _fileProvider.Watch(configFileLoader.ConfigFileName); - return new FileBaseProxyConfig - { - Routes = routes, - Clusters = clusters, - ChangeToken = changeToken - }; + return new FileBaseProxyConfig + { + Routes = routes, + Clusters = clusters, + ChangeToken = changeToken + }; + } } private class FileBaseProxyConfig : IProxyConfig diff --git a/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs index 5483119..51613dd 100644 --- a/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs +++ b/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs @@ -1,6 +1,4 @@ -using System.Text.Json; - -using Yarp.ReverseProxy.Configuration; +using Yarp.ReverseProxy.Configuration; namespace AppServiceProxy.Configuration { @@ -12,9 +10,9 @@ internal class YarpJsonConfigFileLoader : IConfigFileLoader { try { - var yarp = JsonSerializer.Deserialize(contents); + var yarp = YarpJsonReader.ParseJson(contents); - return (yarp?.Routes ?? Array.Empty(), yarp?.Clusters ?? Array.Empty()); + return (yarp.Routes, yarp.Clusters); } catch { diff --git a/AppServiceProxy/Configuration/YarpJsonReader.cs b/AppServiceProxy/Configuration/YarpJsonReader.cs new file mode 100644 index 0000000..7b592b2 --- /dev/null +++ b/AppServiceProxy/Configuration/YarpJsonReader.cs @@ -0,0 +1,55 @@ +using System.Text.Json; + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration +{ + internal class YarpJsonReader + { + public static YarpJson ParseJson(string json) + { + var document = JsonDocument.Parse(json); + + var routes = document.RootElement.GetProperty("Routes"); + var clusters = document.RootElement.GetProperty("Clusters"); + + return new YarpJson { }; + } + + private static RouteConfig ParseRouteConfig(JsonProperty route) + { + return new RouteConfig + { + RouteId = route.Name, + Order = route.Value.TryGetProperty(nameof(RouteConfig.Order), out var order) ? order.GetInt32() : null, + ClusterId = route.Value.TryGetProperty(nameof(RouteConfig.ClusterId), out var clusterId) ? clusterId.GetString() : null, + AuthorizationPolicy = route.Value.TryGetProperty(nameof(RouteConfig.AuthorizationPolicy), out var authorizationPolicy) ? authorizationPolicy.GetString() : null, + CorsPolicy = route.Value.TryGetProperty(nameof(RouteConfig.CorsPolicy), out var corsPolicy) ? corsPolicy.GetString() : null, + Metadata = route.Value.TryGetProperty(nameof(RouteConfig.Metadata), out var metadata) ? metadata.Deserialize>() : null, + Transforms = route.Value.TryGetProperty(nameof(RouteConfig.Transforms), out var transforms) ? ParseTransforms(transforms) : null + }; + } + + private static ClusterConfig ParseClusterConfig(JsonProperty cluster) + { + return new ClusterConfig + { + ClusterId = cluster.Name, + LoadBalancingPolicy = cluster.Value.TryGetProperty(nameof(ClusterConfig.LoadBalancingPolicy), out var loadBalancingPolicy) ? loadBalancingPolicy.GetString() : null, + }; + } + + private static IReadOnlyList>? ParseTransforms(JsonElement transforms) + { + if (transforms.GetArrayLength() == 0) + { + return null; + } + + return transforms.EnumerateArray() + .Select(x => x.EnumerateObject() + .ToDictionary(xs => xs.Name, xs => xs.Value.GetString()!, StringComparer.OrdinalIgnoreCase)) + .ToList(); + } + } +} diff --git a/README.md b/README.md index 422ddea..11f82f9 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,36 @@ See also [App Service Proxy Terraform module](https://github.com/shibayan/terraf ## Usage +### YARP + +Create `yarp.json` info `wwwroot` directory. + +```json +{ + "Routes": { + "route1" : { + "ClusterId": "cluster1", + "Match": { + "Path": "{**catch-all}" + } + } + }, + "Clusters": { + "cluster1": { + "Destinations": { + "cluster1/destination1": { + "Address": "https://shibayan.jp/" + } + } + } + } +} +``` + +- https://microsoft.github.io/reverse-proxy/articles/config-files.html + +### Azure Functions Proxies + Create `proxies.json` into `wwwroot` directory. ```json @@ -88,8 +118,6 @@ Create `proxies.json` into `wwwroot` directory. } ``` -## Appendix: `proxies.json` Reference - - [Advanced configuration - Work with proxies in Azure Functions | Microsoft Docs](https://docs.microsoft.com/en-us/azure/azure-functions/functions-proxies#advanced-configuration) ## License From dd304f08db3e5214ed50737a7b28852f3f446da3 Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Mon, 8 Aug 2022 15:59:29 +0900 Subject: [PATCH 5/5] Improvement namespace structure --- .../ProxiesJsonReaderTests.cs | 2 +- .../ProxiesJsonTransformTests.cs | 2 +- AppServiceProxy.Tests/YarpJsonReaderTests.cs | 21 ++-- .../FileBaseProxyConfigProvider.cs | 102 ++++++++++-------- .../Configuration/IConfigFileLoader.cs | 2 +- .../{ => Proxies}/ProxiesJson.cs | 2 +- .../Proxies/ProxiesJsonConfigFileLoader.cs | 24 +++++ .../{ => Proxies}/ProxiesJsonReader.cs | 6 +- .../{ => Proxies}/ProxiesJsonTransform.cs | 4 +- .../ProxiesJsonConfigFileLoader.cs | 24 ----- .../Configuration/Yarp/YarpJson.cs | 11 ++ .../Yarp/YarpJsonConfigFileLoader.cs | 23 ++++ .../Configuration/Yarp/YarpJsonReader.cs | 53 +++++++++ AppServiceProxy/Configuration/YarpJson.cs | 11 -- .../Configuration/YarpJsonConfigFileLoader.cs | 23 ---- .../Configuration/YarpJsonReader.cs | 55 ---------- AppServiceProxy/Program.cs | 2 + 17 files changed, 187 insertions(+), 180 deletions(-) rename AppServiceProxy/Configuration/{ => Proxies}/ProxiesJson.cs (95%) create mode 100644 AppServiceProxy/Configuration/Proxies/ProxiesJsonConfigFileLoader.cs rename AppServiceProxy/Configuration/{ => Proxies}/ProxiesJsonReader.cs (95%) rename AppServiceProxy/Configuration/{ => Proxies}/ProxiesJsonTransform.cs (97%) delete mode 100644 AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs create mode 100644 AppServiceProxy/Configuration/Yarp/YarpJson.cs create mode 100644 AppServiceProxy/Configuration/Yarp/YarpJsonConfigFileLoader.cs create mode 100644 AppServiceProxy/Configuration/Yarp/YarpJsonReader.cs delete mode 100644 AppServiceProxy/Configuration/YarpJson.cs delete mode 100644 AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs delete mode 100644 AppServiceProxy/Configuration/YarpJsonReader.cs diff --git a/AppServiceProxy.Tests/ProxiesJsonReaderTests.cs b/AppServiceProxy.Tests/ProxiesJsonReaderTests.cs index 3ed8a07..d551715 100644 --- a/AppServiceProxy.Tests/ProxiesJsonReaderTests.cs +++ b/AppServiceProxy.Tests/ProxiesJsonReaderTests.cs @@ -1,4 +1,4 @@ -using AppServiceProxy.Configuration; +using AppServiceProxy.Configuration.Proxies; using Xunit; diff --git a/AppServiceProxy.Tests/ProxiesJsonTransformTests.cs b/AppServiceProxy.Tests/ProxiesJsonTransformTests.cs index 829afe1..0e7e23a 100644 --- a/AppServiceProxy.Tests/ProxiesJsonTransformTests.cs +++ b/AppServiceProxy.Tests/ProxiesJsonTransformTests.cs @@ -1,6 +1,6 @@ using System; -using AppServiceProxy.Configuration; +using AppServiceProxy.Configuration.Proxies; using Xunit; diff --git a/AppServiceProxy.Tests/YarpJsonReaderTests.cs b/AppServiceProxy.Tests/YarpJsonReaderTests.cs index 8251e2c..c8c0a62 100644 --- a/AppServiceProxy.Tests/YarpJsonReaderTests.cs +++ b/AppServiceProxy.Tests/YarpJsonReaderTests.cs @@ -1,15 +1,15 @@ -using AppServiceProxy.Configuration; +using AppServiceProxy.Configuration.Yarp; using Xunit; -namespace AppServiceProxy.Tests +namespace AppServiceProxy.Tests; + +public class YarpJsonReaderTests { - public class YarpJsonReaderTests + [Fact] + public void Basic() { - [Fact] - public void Basic() - { - var json = @" + var json = @" { ""Routes"": { ""route1"": { @@ -31,10 +31,9 @@ public void Basic() } "; - var yarp = YarpJsonReader.ParseJson(json); + var yarp = YarpJsonReader.ParseJson(json); - Assert.Equal(1, yarp.Routes.Count); - Assert.Equal(1, yarp.Clusters.Count); - } + Assert.Equal(1, yarp.Routes.Count); + Assert.Equal(1, yarp.Clusters.Count); } } diff --git a/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs index 2e456bd..2b34e5a 100644 --- a/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs +++ b/AppServiceProxy/Configuration/FileBaseProxyConfigProvider.cs @@ -1,69 +1,77 @@ -using Microsoft.Extensions.FileProviders; +using System.Diagnostics.CodeAnalysis; + +using AppServiceProxy.Configuration.Yarp; + +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Yarp.ReverseProxy.Configuration; -namespace AppServiceProxy.Configuration +namespace AppServiceProxy.Configuration; + +internal class FileBaseProxyConfigProvider : IProxyConfigProvider { - internal class FileBaseProxyConfigProvider : IProxyConfigProvider + public FileBaseProxyConfigProvider(IEnumerable configFileLoaders) { - public FileBaseProxyConfigProvider(IEnumerable configFileLoaders) - { - _configFileLoaders = configFileLoaders; + _configFileLoaders = configFileLoaders; - var json = System.Text.Json.JsonSerializer.Deserialize(File.ReadAllText(@"C:\Users\shibayan\Documents\yarp.json")); + var json = System.Text.Json.JsonSerializer.Deserialize(File.ReadAllText(@"C:\Users\shibayan\Documents\yarp.json")); - _fileProvider = new PhysicalFileProvider(_wwwroot) - { - UseActivePolling = true, - UsePollingFileWatcher = true - }; - } + _fileProvider = new PhysicalFileProvider(s_wwwroot) + { + UseActivePolling = true, + UsePollingFileWatcher = true + }; + } - private readonly IEnumerable _configFileLoaders; - private readonly PhysicalFileProvider _fileProvider; + private readonly IEnumerable _configFileLoaders; + private readonly PhysicalFileProvider _fileProvider; - private static readonly string _wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); + private static readonly string s_wwwroot = Environment.ExpandEnvironmentVariables(@"%HOME%\site\wwwroot"); - public IProxyConfig GetConfig() + public IProxyConfig GetConfig() + { + if (TryGetConfigFileLoader(out var configFileLoader)) { - var configFileLoader = _configFileLoaders.FirstOrDefault(x => File.Exists(Path.Combine(_wwwroot, x.ConfigFileName))); + var contents = File.ReadAllText(Path.Combine(s_wwwroot, configFileLoader.ConfigFileName)); - if (configFileLoader is null) - { - var changeToken = _fileProvider.Watch("*.*"); - - return new FileBaseProxyConfig - { - Routes = Array.Empty(), - Clusters = Array.Empty(), - ChangeToken = changeToken - }; - } - else - { - var contents = File.ReadAllText(Path.Combine(_wwwroot, configFileLoader.ConfigFileName)); + var (routes, clusters) = configFileLoader.LoadConfig(contents); - var (routes, clusters) = configFileLoader.LoadConfig(contents); + var changeToken = _fileProvider.Watch(configFileLoader.ConfigFileName); - var changeToken = _fileProvider.Watch(configFileLoader.ConfigFileName); + return new FileBaseProxyConfig + { + Routes = routes, + Clusters = clusters, + ChangeToken = changeToken + }; + } + else + { + var changeToken = _fileProvider.Watch("*.*"); - return new FileBaseProxyConfig - { - Routes = routes, - Clusters = clusters, - ChangeToken = changeToken - }; - } + return new FileBaseProxyConfig + { + Routes = Array.Empty(), + Clusters = Array.Empty(), + ChangeToken = changeToken + }; } + } - private class FileBaseProxyConfig : IProxyConfig - { - public IReadOnlyList Routes { get; init; } = null!; + private bool TryGetConfigFileLoader([NotNullWhen(true)] out IConfigFileLoader? configFileLoader) + { + configFileLoader = _configFileLoaders.FirstOrDefault(x => File.Exists(Path.Combine(s_wwwroot, x.ConfigFileName))); - public IReadOnlyList Clusters { get; init; } = null!; + return configFileLoader is not null; + } - public IChangeToken ChangeToken { get; init; } = null!; - } + private class FileBaseProxyConfig : IProxyConfig + { + public IReadOnlyList Routes { get; init; } = null!; + + public IReadOnlyList Clusters { get; init; } = null!; + + public IChangeToken ChangeToken { get; init; } = null!; } } diff --git a/AppServiceProxy/Configuration/IConfigFileLoader.cs b/AppServiceProxy/Configuration/IConfigFileLoader.cs index b4f1f75..56d9e2e 100644 --- a/AppServiceProxy/Configuration/IConfigFileLoader.cs +++ b/AppServiceProxy/Configuration/IConfigFileLoader.cs @@ -7,4 +7,4 @@ internal interface IConfigFileLoader string ConfigFileName { get; } (IReadOnlyList, IReadOnlyList) LoadConfig(string contents); -} \ No newline at end of file +} diff --git a/AppServiceProxy/Configuration/ProxiesJson.cs b/AppServiceProxy/Configuration/Proxies/ProxiesJson.cs similarity index 95% rename from AppServiceProxy/Configuration/ProxiesJson.cs rename to AppServiceProxy/Configuration/Proxies/ProxiesJson.cs index 7c94c29..b62764f 100644 --- a/AppServiceProxy/Configuration/ProxiesJson.cs +++ b/AppServiceProxy/Configuration/Proxies/ProxiesJson.cs @@ -1,4 +1,4 @@ -namespace AppServiceProxy.Configuration; +namespace AppServiceProxy.Configuration.Proxies; internal class ProxiesJson { diff --git a/AppServiceProxy/Configuration/Proxies/ProxiesJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/Proxies/ProxiesJsonConfigFileLoader.cs new file mode 100644 index 0000000..62299dc --- /dev/null +++ b/AppServiceProxy/Configuration/Proxies/ProxiesJsonConfigFileLoader.cs @@ -0,0 +1,24 @@ + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration.Proxies; + +internal class ProxiesJsonConfigFileLoader : IConfigFileLoader +{ + public string ConfigFileName => "proxies.json"; + + public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) + { + try + { + var proxies = ProxiesJsonReader.ParseJson(contents); + var (routes, clusters) = ProxiesJsonTransform.Apply(proxies); + + return (routes, clusters); + } + catch + { + return (Array.Empty(), Array.Empty()); + } + } +} diff --git a/AppServiceProxy/Configuration/ProxiesJsonReader.cs b/AppServiceProxy/Configuration/Proxies/ProxiesJsonReader.cs similarity index 95% rename from AppServiceProxy/Configuration/ProxiesJsonReader.cs rename to AppServiceProxy/Configuration/Proxies/ProxiesJsonReader.cs index ad165e6..06e7f82 100644 --- a/AppServiceProxy/Configuration/ProxiesJsonReader.cs +++ b/AppServiceProxy/Configuration/Proxies/ProxiesJsonReader.cs @@ -1,8 +1,8 @@ -using System.Text.Json; +using System.Text.Json; -using static AppServiceProxy.Configuration.ProxiesJson; +using static AppServiceProxy.Configuration.Proxies.ProxiesJson; -namespace AppServiceProxy.Configuration; +namespace AppServiceProxy.Configuration.Proxies; internal class ProxiesJsonReader { diff --git a/AppServiceProxy/Configuration/ProxiesJsonTransform.cs b/AppServiceProxy/Configuration/Proxies/ProxiesJsonTransform.cs similarity index 97% rename from AppServiceProxy/Configuration/ProxiesJsonTransform.cs rename to AppServiceProxy/Configuration/Proxies/ProxiesJsonTransform.cs index 725cbb6..edbb58e 100644 --- a/AppServiceProxy/Configuration/ProxiesJsonTransform.cs +++ b/AppServiceProxy/Configuration/Proxies/ProxiesJsonTransform.cs @@ -2,9 +2,9 @@ using Yarp.ReverseProxy.Configuration; -using static AppServiceProxy.Configuration.ProxiesJson; +using static AppServiceProxy.Configuration.Proxies.ProxiesJson; -namespace AppServiceProxy.Configuration; +namespace AppServiceProxy.Configuration.Proxies; internal static class ProxiesJsonTransform { diff --git a/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs deleted file mode 100644 index 2d21fb0..0000000 --- a/AppServiceProxy/Configuration/ProxiesJsonConfigFileLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class ProxiesJsonConfigFileLoader : IConfigFileLoader - { - public string ConfigFileName => "proxies.json"; - - public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) - { - try - { - var proxies = ProxiesJsonReader.ParseJson(contents); - var (routes, clusters) = ProxiesJsonTransform.Apply(proxies); - - return (routes, clusters); - } - catch - { - return (Array.Empty(), Array.Empty()); - } - } - } -} diff --git a/AppServiceProxy/Configuration/Yarp/YarpJson.cs b/AppServiceProxy/Configuration/Yarp/YarpJson.cs new file mode 100644 index 0000000..6293b4a --- /dev/null +++ b/AppServiceProxy/Configuration/Yarp/YarpJson.cs @@ -0,0 +1,11 @@ + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration.Yarp; + +internal class YarpJson +{ + public IReadOnlyList Routes { get; set; } = null!; + + public IReadOnlyList Clusters { get; set; } = null!; +} diff --git a/AppServiceProxy/Configuration/Yarp/YarpJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/Yarp/YarpJsonConfigFileLoader.cs new file mode 100644 index 0000000..4b62335 --- /dev/null +++ b/AppServiceProxy/Configuration/Yarp/YarpJsonConfigFileLoader.cs @@ -0,0 +1,23 @@ + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration.Yarp; + +internal class YarpJsonConfigFileLoader : IConfigFileLoader +{ + public string ConfigFileName => "yarp.json"; + + public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) + { + try + { + var yarp = YarpJsonReader.ParseJson(contents); + + return (yarp.Routes, yarp.Clusters); + } + catch + { + return (Array.Empty(), Array.Empty()); + } + } +} diff --git a/AppServiceProxy/Configuration/Yarp/YarpJsonReader.cs b/AppServiceProxy/Configuration/Yarp/YarpJsonReader.cs new file mode 100644 index 0000000..146d2f6 --- /dev/null +++ b/AppServiceProxy/Configuration/Yarp/YarpJsonReader.cs @@ -0,0 +1,53 @@ +using System.Text.Json; + +using Yarp.ReverseProxy.Configuration; + +namespace AppServiceProxy.Configuration.Yarp; + +internal class YarpJsonReader +{ + public static YarpJson ParseJson(string json) + { + var document = JsonDocument.Parse(json); + + var routes = document.RootElement.GetProperty("Routes"); + var clusters = document.RootElement.GetProperty("Clusters"); + + return new YarpJson { }; + } + + private static RouteConfig ParseRouteConfig(JsonProperty route) + { + return new RouteConfig + { + RouteId = route.Name, + Order = route.Value.TryGetProperty(nameof(RouteConfig.Order), out var order) ? order.GetInt32() : null, + ClusterId = route.Value.TryGetProperty(nameof(RouteConfig.ClusterId), out var clusterId) ? clusterId.GetString() : null, + AuthorizationPolicy = route.Value.TryGetProperty(nameof(RouteConfig.AuthorizationPolicy), out var authorizationPolicy) ? authorizationPolicy.GetString() : null, + CorsPolicy = route.Value.TryGetProperty(nameof(RouteConfig.CorsPolicy), out var corsPolicy) ? corsPolicy.GetString() : null, + Metadata = route.Value.TryGetProperty(nameof(RouteConfig.Metadata), out var metadata) ? metadata.Deserialize>() : null, + Transforms = route.Value.TryGetProperty(nameof(RouteConfig.Transforms), out var transforms) ? ParseTransforms(transforms) : null + }; + } + + private static ClusterConfig ParseClusterConfig(JsonProperty cluster) + { + return new ClusterConfig + { + ClusterId = cluster.Name, + LoadBalancingPolicy = cluster.Value.TryGetProperty(nameof(ClusterConfig.LoadBalancingPolicy), out var loadBalancingPolicy) ? loadBalancingPolicy.GetString() : null, + }; + } + + private static IReadOnlyList>? ParseTransforms(JsonElement transforms) + { + if (transforms.GetArrayLength() == 0) + { + return null; + } + + return transforms.EnumerateArray() + .Select(x => x.EnumerateObject().ToDictionary(xs => xs.Name, xs => xs.Value.GetString()!, StringComparer.OrdinalIgnoreCase)) + .ToList(); + } +} diff --git a/AppServiceProxy/Configuration/YarpJson.cs b/AppServiceProxy/Configuration/YarpJson.cs deleted file mode 100644 index b1b6af3..0000000 --- a/AppServiceProxy/Configuration/YarpJson.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class YarpJson - { - public IReadOnlyList Routes { get; set; } = null!; - - public IReadOnlyList Clusters { get; set; } = null!; - } -} diff --git a/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs b/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs deleted file mode 100644 index 51613dd..0000000 --- a/AppServiceProxy/Configuration/YarpJsonConfigFileLoader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class YarpJsonConfigFileLoader : IConfigFileLoader - { - public string ConfigFileName => "yarp.json"; - - public (IReadOnlyList, IReadOnlyList) LoadConfig(string contents) - { - try - { - var yarp = YarpJsonReader.ParseJson(contents); - - return (yarp.Routes, yarp.Clusters); - } - catch - { - return (Array.Empty(), Array.Empty()); - } - } - } -} diff --git a/AppServiceProxy/Configuration/YarpJsonReader.cs b/AppServiceProxy/Configuration/YarpJsonReader.cs deleted file mode 100644 index 7b592b2..0000000 --- a/AppServiceProxy/Configuration/YarpJsonReader.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Text.Json; - -using Yarp.ReverseProxy.Configuration; - -namespace AppServiceProxy.Configuration -{ - internal class YarpJsonReader - { - public static YarpJson ParseJson(string json) - { - var document = JsonDocument.Parse(json); - - var routes = document.RootElement.GetProperty("Routes"); - var clusters = document.RootElement.GetProperty("Clusters"); - - return new YarpJson { }; - } - - private static RouteConfig ParseRouteConfig(JsonProperty route) - { - return new RouteConfig - { - RouteId = route.Name, - Order = route.Value.TryGetProperty(nameof(RouteConfig.Order), out var order) ? order.GetInt32() : null, - ClusterId = route.Value.TryGetProperty(nameof(RouteConfig.ClusterId), out var clusterId) ? clusterId.GetString() : null, - AuthorizationPolicy = route.Value.TryGetProperty(nameof(RouteConfig.AuthorizationPolicy), out var authorizationPolicy) ? authorizationPolicy.GetString() : null, - CorsPolicy = route.Value.TryGetProperty(nameof(RouteConfig.CorsPolicy), out var corsPolicy) ? corsPolicy.GetString() : null, - Metadata = route.Value.TryGetProperty(nameof(RouteConfig.Metadata), out var metadata) ? metadata.Deserialize>() : null, - Transforms = route.Value.TryGetProperty(nameof(RouteConfig.Transforms), out var transforms) ? ParseTransforms(transforms) : null - }; - } - - private static ClusterConfig ParseClusterConfig(JsonProperty cluster) - { - return new ClusterConfig - { - ClusterId = cluster.Name, - LoadBalancingPolicy = cluster.Value.TryGetProperty(nameof(ClusterConfig.LoadBalancingPolicy), out var loadBalancingPolicy) ? loadBalancingPolicy.GetString() : null, - }; - } - - private static IReadOnlyList>? ParseTransforms(JsonElement transforms) - { - if (transforms.GetArrayLength() == 0) - { - return null; - } - - return transforms.EnumerateArray() - .Select(x => x.EnumerateObject() - .ToDictionary(xs => xs.Name, xs => xs.Value.GetString()!, StringComparer.OrdinalIgnoreCase)) - .ToList(); - } - } -} diff --git a/AppServiceProxy/Program.cs b/AppServiceProxy/Program.cs index 84ca9de..7cab895 100644 --- a/AppServiceProxy/Program.cs +++ b/AppServiceProxy/Program.cs @@ -1,4 +1,6 @@ using AppServiceProxy.Configuration; +using AppServiceProxy.Configuration.Proxies; +using AppServiceProxy.Configuration.Yarp; using Microsoft.Extensions.DependencyInjection.Extensions;