Yarp.ReverseProxy
Alessio Meroni
Posted on October 11, 2024
Hi,
I anticipate the network structure. There is a virtual network, where there is a cloud server on which an application with YARP Reverse Proxy is installed. This is in VPN with x gateway on which a local application will run, not on the network. These local applications can be reached by external clients following access to the application on the cloud server.
I have to modify the application on the cloud server so that the user, specifying IP and Port, can connect to the final gateway. The user must be able to navigate the final application. In the reverse proxy you need to manage this dynamic, that is, that I don't have to specify and modify all the various options.
Currently, by entering IP and Port, the reverse proxy shows me the address with part of the path. For example, if I go to contuoso.com/Home/Proxy
following a previous form, I am redirected to http://{Ip}:{Port}/Home/Proxy
.
It must be ensured that by entering the final address the user can then navigate independently. For example, open the login on the application locally: http://10.10.0.2/Users/Login
.
Below I leave the current code that should be changed:
HomeController.cs
[HttpGet]
public IActionResult Status()
{
return View();
}
[HttpPost]
public IActionResult SetProxy(string ip, string port)
{
if (string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(port))
{
ViewBag.Error = "IP e Porta sono obbligatori.";
return View();
}
string targetUrl = $"http://{ip}:{port}";
HttpContext.Session.SetString("TargetUrl", targetUrl);
// Log the target URL
_logger.LogInformation($"Target URL set to: {targetUrl}");
// Aggiorna la configurazione del proxy
var routes = new List<RouteConfig>
{
new RouteConfig
{
RouteId = "proxyRoute",
ClusterId = "backendCluster",
Match = new RouteMatch
{
// Inoltra tutte le richieste senza rimuovere o modificare il percorso
Path = "/{**catch-all}"
}
}
};
var clusters = new List<ClusterConfig>
{
new ClusterConfig
{
ClusterId = "backendCluster",
Destinations = new Dictionary<string, DestinationConfig>
{
{ "destination1", new DestinationConfig { Address = targetUrl } }
}
}
};
_customProxyConfigProvider.UpdateConfig(routes, clusters);
return RedirectToAction("Proxy");
}
public async Task Proxy()
{
var targetUrl = HttpContext.Session.GetString("TargetUrl");
if (string.IsNullOrEmpty(targetUrl))
{
Response.StatusCode = 400;
await Response.WriteAsync("Target URL non configurato.");
return;
}
// Log the target URL before forwarding
_logger.LogInformation($"Forwarding request to: {targetUrl}");
var httpContext = HttpContext;
var forwarderRequestConfig = new ForwarderRequestConfig();
var error = await _httpForwarder.SendAsync(httpContext, targetUrl, _httpClient, forwarderRequestConfig);
if (error != ForwarderError.None)
{
var errorFeature = httpContext.GetForwarderErrorFeature();
var exception = errorFeature?.Exception;
// Gestisci l'errore di inoltro
_logger.LogError($"Errore di inoltro: {error}, eccezione: {exception?.Message}");
Response.StatusCode = 500;
await Response.WriteAsync($"Errore di inoltro: {error}, eccezione: {exception?.Message}");
}
}
Program.cs
builder.Services.AddSingleton<CustomProxyConfigProvider>();
builder.Services.AddSingleton<IProxyConfigProvider>(sp => sp.GetRequiredService<CustomProxyConfigProvider>());
builder.Services.AddReverseProxy();
builder.Services.AddSession();
builder.Services.AddHttpForwarder();
builder.Services.AddSingleton(new HttpMessageInvoker(new SocketsHttpHandler()));
...
app.MapControllers();
app.MapReverseProxy();`
CustomProxyConfigProvider
public class CustomProxyConfigProvider : IProxyConfigProvider
{
private volatile CustomProxyConfig _config;
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public CustomProxyConfigProvider()
{
_config = new CustomProxyConfig(new List<RouteConfig>(), new List<ClusterConfig>());
}
public IProxyConfig GetConfig() => _config;
public void UpdateConfig(List<RouteConfig> routes, List<ClusterConfig> clusters)
{
_config = new CustomProxyConfig(routes, clusters);
_cts.Cancel();
}
private class CustomProxyConfig : IProxyConfig
{
public CustomProxyConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
Routes = routes;
Clusters = clusters;
ChangeToken = new CancellationChangeToken(new CancellationTokenSource().Token);
}
public IReadOnlyList<RouteConfig> Routes { get; }
public IReadOnlyList<ClusterConfig> Clusters { get; }
public IChangeToken ChangeToken { get; }
}
}
Any suggestions?
Posted on October 11, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.