✨ Gateway proxy for contained frontend to access other services
This commit is contained in:
83
DysonNetwork.Shared/Registry/GatewayReverseProxy.cs
Normal file
83
DysonNetwork.Shared/Registry/GatewayReverseProxy.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public static class GatewayReverseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides reverse proxy for DysonNetwork.Gateway.
|
||||
/// Give the ability to the contained frontend to access other services via the gateway.
|
||||
/// </summary>
|
||||
/// <param name="app">The asp.net core application</param>
|
||||
/// <returns>The modified application</returns>
|
||||
public static WebApplication MapGatewayProxy(this WebApplication app)
|
||||
{
|
||||
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler
|
||||
{
|
||||
UseProxy = false,
|
||||
AllowAutoRedirect = true,
|
||||
AutomaticDecompression = DecompressionMethods.All,
|
||||
UseCookies = true,
|
||||
EnableMultipleHttp2Connections = true,
|
||||
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
|
||||
ConnectTimeout = TimeSpan.FromSeconds(15),
|
||||
});
|
||||
|
||||
var transformer = new GatewayReverseProxyTransformer();
|
||||
var requestConfig = new ForwarderRequestConfig();
|
||||
|
||||
app.Map("/cgi/{**catch-all}", async (HttpContext context, IHttpForwarder forwarder) =>
|
||||
{
|
||||
var registry = context.RequestServices.GetRequiredService<ServiceRegistry>();
|
||||
var gatewayUrl = await registry.GetServiceUrl("DysonNetwork.Gateway");
|
||||
if (gatewayUrl is null)
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
await context.Response.WriteAsync("Gateway not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var error = await forwarder.SendAsync(
|
||||
context,
|
||||
gatewayUrl,
|
||||
httpClient,
|
||||
requestConfig,
|
||||
transformer
|
||||
);
|
||||
if (error != ForwarderError.None)
|
||||
{
|
||||
var errorFeature = context.GetForwarderErrorFeature();
|
||||
var exception = errorFeature?.Exception;
|
||||
context.Response.StatusCode = 502;
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync($"Gateway remote error: {exception?.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
public class GatewayReverseProxyTransformer : HttpTransformer
|
||||
{
|
||||
private const string Value = "/cgi";
|
||||
|
||||
public override ValueTask TransformRequestAsync(
|
||||
HttpContext httpContext,
|
||||
HttpRequestMessage proxyRequest,
|
||||
string destinationPrefix,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
httpContext.Request.Path = httpContext.Request.Path.StartsWithSegments(Value, out var remaining)
|
||||
? remaining
|
||||
: httpContext.Request.Path;
|
||||
|
||||
return Default.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public static class ServiceHelper
|
||||
public static class ServiceInjectionHelper
|
||||
{
|
||||
public static IServiceCollection AddPusherService(this IServiceCollection services)
|
||||
{
|
@@ -45,4 +45,11 @@ public class ServiceRegistry(IEtcdClient etcd, ILogger<ServiceRegistry> logger)
|
||||
var key = $"/services/{serviceName}";
|
||||
await etcd.DeleteAsync(key);
|
||||
}
|
||||
|
||||
public async Task<string?> GetServiceUrl(string serviceName)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
var response = await etcd.GetAsync(key);
|
||||
return response.Kvs.Count == 0 ? null : response.Kvs[0].Value.ToStringUtf8();
|
||||
}
|
||||
}
|
@@ -8,7 +8,8 @@ public static class RegistryStartup
|
||||
{
|
||||
public static IServiceCollection AddRegistryService(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration
|
||||
IConfiguration configuration,
|
||||
bool addForwarder = true
|
||||
)
|
||||
{
|
||||
services.AddEtcdClient(options =>
|
||||
@@ -19,6 +20,9 @@ public static class RegistryStartup
|
||||
services.AddSingleton<ServiceRegistry>();
|
||||
services.AddHostedService<RegistryHostedService>();
|
||||
|
||||
if (addForwarder)
|
||||
services.AddHttpForwarder();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user