(null);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Auth.Sample/Views/Account/AccessDenied.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Access Denied";
3 | }
4 |
5 |
9 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Auth.Sample/Views/Account/LoggedOut.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Logged Out";
3 | }
4 | You have been logged out.
5 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Auth.Sample/Views/Account/Login.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Login";
3 | }
4 |
5 | Login
6 |
7 |
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Auth.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Auth.Sample/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/samples/ReverseProxy.Auth.Sample/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/samples/ReverseProxy.Code.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Code.Sample": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Code.Sample/ReverseProxy.Code.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Code.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Code.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Config.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | builder.Services.AddControllers();
10 | builder.Services.AddReverseProxy()
11 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
12 |
13 | var app = builder.Build();
14 |
15 | app.UseCors();
16 | app.MapReverseProxy();
17 |
18 | app.Run();
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Config.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Config.Sample": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Config.Sample/ReverseProxy.Config.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Config.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.ConfigFilter.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Yarp.Sample;
7 |
8 | var builder = WebApplication.CreateBuilder(args);
9 |
10 | builder.Services.AddControllers();
11 | builder.Services.AddReverseProxy()
12 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
13 | .AddConfigFilter();
14 |
15 | var app = builder.Build();
16 |
17 | app.MapReverseProxy();
18 |
19 | app.Run();
20 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.ConfigFilter.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.ConfigFilter.Sample": {
12 | "commandName": "Project",
13 | "environmentVariables": {
14 | "Key": "Value",
15 | "contoso": "https://contoso.com",
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "IIS Express": {
20 | "commandName": "IISExpress",
21 | "launchBrowser": true,
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.ConfigFilter.Sample/ReverseProxy.ConfigFilter.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.ConfigFilter.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Direct.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Direct.Sample": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Direct.Sample/ReverseProxy.Direct.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Direct.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Direct.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Kestrel": {
11 | "Endpoints": {
12 | "https": {
13 | "Url": "https://localhost:5001"
14 | },
15 | "http": {
16 | "Url": "http://localhost:5000"
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/ReverseProxy/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Diagnostics;
5 |
6 | var builder = WebApplication.CreateBuilder(args);
7 |
8 | Debug.Assert(OperatingSystem.IsWindows());
9 | builder.WebHost.UseHttpSys();
10 | builder.Services.AddReverseProxy()
11 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
12 |
13 | var app = builder.Build();
14 |
15 | app.MapReverseProxy(proxyPipeline =>
16 | {
17 | proxyPipeline.UseSessionAffinity(); // Has no effect on delegation destinations because the response doesn't go through YARP
18 | proxyPipeline.UseLoadBalancing();
19 | proxyPipeline.UsePassiveHealthChecks();
20 | proxyPipeline.UseHttpSysDelegation();
21 | });
22 |
23 | app.Run();
24 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/ReverseProxy/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "ReverseProxy.HttpSysDelegation": {
4 | "commandName": "Project",
5 | "dotnetRunMessages": true,
6 | "launchBrowser": true,
7 | "applicationUrl": "http://localhost:5500",
8 | "environmentVariables": {
9 | "ASPNETCORE_ENVIRONMENT": "Development"
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/ReverseProxy/ReverseProxy.HttpSysDelegation.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/ReverseProxy/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/SampleHttpSysServer/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Diagnostics;
5 | using Microsoft.AspNetCore.Server.HttpSys;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | Debug.Assert(OperatingSystem.IsWindows());
10 | builder.WebHost.UseHttpSys(options =>
11 | {
12 | options.RequestQueueName = "SampleHttpSysServerQueue";
13 | options.RequestQueueMode = RequestQueueMode.Create;
14 | });
15 |
16 | var app = builder.Build();
17 |
18 | app.Run(async context =>
19 | {
20 | await context.Response.WriteAsync($"Hello World! (PID: {Environment.ProcessId})");
21 | });
22 | app.Run();
23 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/SampleHttpSysServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "SampleHttpSysServer": {
4 | "commandName": "Project",
5 | "dotnetRunMessages": true,
6 | "launchBrowser": true,
7 | "applicationUrl": "http://localhost:5600",
8 | "environmentVariables": {
9 | "ASPNETCORE_ENVIRONMENT": "Development"
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/SampleHttpSysServer/SampleHttpSysServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | enable
6 | enable
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/SampleHttpSysServer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.HttpSysDelegation.Sample/SampleHttpSysServer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.LetsEncrypt.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | builder.Services.AddLettuceEncrypt();
10 |
11 | builder.Services.AddControllers();
12 | // Add the reverse proxy capability to the server
13 | builder.Services.AddReverseProxy()
14 | // Initialize the reverse proxy from the "ReverseProxy" section of configuration
15 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
16 |
17 | var app = builder.Build();
18 |
19 | // Register the reverse proxy routes
20 | app.MapReverseProxy();
21 |
22 | app.Run();
23 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.LetsEncrypt.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "ReverseProxy.LetsEncrypt.Sample": {
5 | "commandName": "Project",
6 | "launchBrowser": false,
7 | "launchUrl": "",
8 | "environmentVariables": {
9 | "ASPNETCORE_ENVIRONMENT": "Development"
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.LetsEncrypt.Sample/README.md:
--------------------------------------------------------------------------------
1 | # Lets Encrypt Sample
2 |
3 | [Lets Encrypt](https://letsencrypt.org/) is a certificate authority (CA) that provides HTTPS (SSL/TLS) certificates for free. This sample shows how to add Lets Encrypt for TLS termination in YARP by integrating with [LettuceEncrypt](https://github.com/natemcmaster/LettuceEncrypt). It allows to set up TLS between the client and YARP with minimal configuration.
4 |
5 | The sample includes the following parts:
6 |
7 | - ### [Program.cs](Program.cs)
8 | It calls `IServiceCollection.AddLettuceEncrypt` in the `ConfigureServices` method.
9 |
10 | - ### [appsettings.json](appsettings.json)
11 | Sets up the required options for LettuceEncrypt including:
12 | - "DomainNames" - at least one domain name is required
13 | - "EmailAddress" - email address must be specified to register with the certificate authority
14 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.LetsEncrypt.Sample/ReverseProxy.LetsEncrypt.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Metrics.Sample/ForwarderMetricsConsumer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using Yarp.Telemetry.Consumption;
6 |
7 | namespace Yarp.Sample
8 | {
9 | public sealed class ForwarderMetricsConsumer : IMetricsConsumer
10 | {
11 | public void OnMetrics(ForwarderMetrics previous, ForwarderMetrics current)
12 | {
13 | var elapsed = current.Timestamp - previous.Timestamp;
14 | var newRequests = current.RequestsStarted - previous.RequestsStarted;
15 | Console.Title = $"Forwarded {current.RequestsStarted} requests ({newRequests} in the last {(int)elapsed.TotalMilliseconds} ms)";
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Metrics.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Metrics.Sample": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Metrics.Sample/ReverseProxy.Metrics.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Metrics.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Metrics.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | // "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Kestrel": {
11 | "Endpoints": {
12 | "http": {
13 | "Url": "http://localhost:5000"
14 | },
15 | "https": {
16 | "Url": "https://localhost:5001"
17 | }
18 | }
19 | },
20 | "ReverseProxy": {
21 | "Routes": {
22 | "route1": {
23 | "ClusterId": "cluster1",
24 | "Match": {
25 | "Path": "{**catch-all}"
26 | }
27 | }
28 | },
29 | "Clusters": {
30 | "cluster1": {
31 | "Destinations": {
32 | "cluster1/destination1": {
33 | "Address": "https://example.com/"
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Transforms.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Transforms.Sample": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Transforms.Sample/ReverseProxy.Transforms.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | Yarp.Sample
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Transforms.Sample/TokenService.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Security.Claims;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.Sample
8 | {
9 | internal sealed class TokenService
10 | {
11 | internal Task GetAuthTokenAsync(ClaimsPrincipal user)
12 | {
13 | return Task.FromResult(user.Identity.Name);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/samples/ReverseProxy.Transforms.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/SampleServer/Controllers/HealthController.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace SampleServer.Controllers
7 | {
8 | ///
9 | /// Controller for active health check probes.
10 | ///
11 | [ApiController]
12 | public class HealthController : ControllerBase
13 | {
14 | private static volatile int _count;
15 | ///
16 | /// Returns 200 if server is healthy.
17 | ///
18 | [HttpGet]
19 | [Route("/api/health")]
20 | public IActionResult CheckHealth()
21 | {
22 | _count++;
23 | // Simulate temporary health degradation.
24 | return _count % 10 < 4 ? Ok() : StatusCode(500);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/SampleServer/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | builder.Services.AddControllers()
10 | .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
11 |
12 | var app = builder.Build();
13 |
14 | app.UseWebSockets();
15 | app.MapControllers();
16 |
17 | app.Run();
18 |
--------------------------------------------------------------------------------
/samples/SampleServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "SampleServer": {
4 | "commandName": "Project",
5 | "launchBrowser": false,
6 | "environmentVariables": {
7 | "ASPNETCORE_ENVIRONMENT": "Development"
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/samples/SampleServer/SampleServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(ReleaseTFMs)
5 | Exe
6 | SampleServer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/SampleServer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Information",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/SampleServer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
--------------------------------------------------------------------------------
/src/Common/Package.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Caching/Endpoints.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s.Models;
5 | using System;
6 | using System.Collections.Generic;
7 |
8 | namespace Yarp.Kubernetes.Controller.Caching;
9 |
10 | ///
11 | /// Holds data needed from a resource.
12 | ///
13 | public struct Endpoints
14 | {
15 | public Endpoints(V1Endpoints endpoints)
16 | {
17 | if (endpoints is null)
18 | {
19 | throw new ArgumentNullException(nameof(endpoints));
20 | }
21 |
22 | Name = endpoints.Name();
23 | Subsets = endpoints.Subsets;
24 | }
25 |
26 | public string Name { get; set; }
27 | public IList Subsets { get; }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Caching/IngressData.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s.Models;
5 | using System;
6 |
7 | namespace Yarp.Kubernetes.Controller.Caching;
8 |
9 | ///
10 | /// Holds data needed from a resource.
11 | ///
12 | public struct IngressData
13 | {
14 | public IngressData(V1Ingress ingress)
15 | {
16 | if (ingress is null)
17 | {
18 | throw new ArgumentNullException(nameof(ingress));
19 | }
20 |
21 | Spec = ingress.Spec;
22 | Metadata = ingress.Metadata;
23 | }
24 |
25 | public V1IngressSpec Spec { get; set; }
26 | public V1ObjectMeta Metadata { get; set; }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Caching/ServiceData.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s.Models;
5 | using System;
6 |
7 | namespace Yarp.Kubernetes.Controller.Caching;
8 |
9 | ///
10 | /// Holds data needed from a resource.
11 | ///
12 | public struct ServiceData
13 | {
14 | public ServiceData(V1Service service)
15 | {
16 | if (service is null)
17 | {
18 | throw new ArgumentNullException(nameof(service));
19 | }
20 |
21 | Spec = service.Spec;
22 | Metadata = service.Metadata;
23 | }
24 |
25 | public V1ServiceSpec Spec { get; set; }
26 | public V1ObjectMeta Metadata { get; set; }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Certificates/ICertificateHelper.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Security.Cryptography.X509Certificates;
5 | using k8s.Models;
6 |
7 | namespace Yarp.Kubernetes.Controller.Certificates;
8 |
9 | public interface ICertificateHelper
10 | {
11 | X509Certificate2 ConvertCertificate(NamespacedName namespacedName, V1Secret secret);
12 | }
13 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Client/IIngressResourceStatusUpdater.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.Kubernetes.Controller.Client;
8 |
9 | public interface IIngressResourceStatusUpdater
10 | {
11 | ///
12 | /// Updates the status of cached ingresses.
13 | ///
14 | Task UpdateStatusAsync(CancellationToken cancellationToken);
15 | }
16 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Client/KubernetesClientOptions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s;
5 |
6 | namespace Yarp.Kubernetes.Controller.Client;
7 |
8 | ///
9 | /// Class KubernetesClientOptions.
10 | ///
11 | public class KubernetesClientOptions
12 | {
13 | ///
14 | /// Gets or sets the configuration.
15 | ///
16 | /// The configuration.
17 | public KubernetesClientConfiguration Configuration { get; set; }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Client/ResourceSelector.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s;
5 | using k8s.Models;
6 |
7 | namespace Yarp.Kubernetes.Controller.Client;
8 |
9 | ///
10 | /// Provides a mechanism for to constrain search based on fields in the resource.
11 | ///
12 | public class ResourceSelector
13 | where TResource : class, IKubernetesObject, new()
14 | {
15 | public ResourceSelector(string fieldSelector)
16 | {
17 | FieldSelector = fieldSelector;
18 | }
19 |
20 | public string FieldSelector { get; }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/ConfigProvider/IUpdateConfig.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Yarp.ReverseProxy.Configuration;
8 |
9 | namespace Yarp.Kubernetes.Controller.Configuration;
10 |
11 | public interface IUpdateConfig
12 | {
13 | Task UpdateAsync(IReadOnlyList routes, IReadOnlyList clusters, CancellationToken cancellationToken);
14 | }
15 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Converters/ClusterTransfer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.ReverseProxy.Configuration;
6 |
7 | namespace Yarp.Kubernetes.Controller.Converters;
8 |
9 | internal sealed class ClusterTransfer
10 | {
11 | public Dictionary Destinations { get; set; } = new Dictionary();
12 | public string ClusterId { get; set; }
13 | public string LoadBalancingPolicy { get; set; }
14 | public SessionAffinityConfig SessionAffinity { get; set; }
15 | public HealthCheckConfig HealthCheck { get; set; }
16 | public HttpClientConfig HttpClientConfig { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Converters/YarpIngressContext.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.Kubernetes.Controller.Caching;
6 |
7 | namespace Yarp.Kubernetes.Controller.Converters;
8 |
9 | internal sealed class YarpIngressContext
10 | {
11 | public YarpIngressContext(IngressData ingress, List services, List endpoints)
12 | {
13 | Ingress = ingress;
14 | Services = services;
15 | Endpoints = endpoints;
16 | }
17 |
18 | public YarpIngressOptions Options { get; set; } = new YarpIngressOptions();
19 | public IngressData Ingress { get; }
20 | public List Services { get; }
21 | public List Endpoints { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | // Copyright (c) .NET Foundation. All rights reserved.
5 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
6 |
7 | using System.Runtime.CompilerServices;
8 |
9 | [assembly: InternalsVisibleTo("Yarp.Kubernetes.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
10 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Protocol/IDispatchTarget.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.Kubernetes.Controller.Dispatching;
8 |
9 | ///
10 | /// IDispatchTarget is what an will use to
11 | /// dispatch information.
12 | ///
13 | public interface IDispatchTarget
14 | {
15 | public Task SendAsync(byte[] utf8Bytes, CancellationToken cancellationToken);
16 | }
17 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Protocol/IDispatcher.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.Kubernetes.Controller.Dispatching;
8 |
9 | ///
10 | /// IDispatcher is a service interface to bridge outgoing data to the
11 | /// current connections.
12 | ///
13 | public interface IDispatcher
14 | {
15 | Task AttachAsync(IDispatchTarget target, CancellationToken cancellationToken);
16 | void Detach(IDispatchTarget target);
17 | Task SendAsync(byte[] utf8Bytes, CancellationToken cancellationToken);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Protocol/Message.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using System.Text.Json.Serialization;
6 | using Yarp.ReverseProxy.Configuration;
7 |
8 | namespace Yarp.Kubernetes.Protocol;
9 |
10 | public enum MessageType
11 | {
12 | Heartbeat,
13 | Update,
14 | Remove,
15 | }
16 |
17 | public struct Message
18 | {
19 | [JsonConverter(typeof(JsonStringEnumConverter))]
20 | public MessageType MessageType { get; set; }
21 |
22 | public string Key { get; set; }
23 |
24 | public List Routes { get; set; }
25 |
26 | public List Cluster { get; set; }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Protocol/ReceiverOptions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Net.Http;
6 |
7 | namespace Yarp.Kubernetes.Protocol;
8 |
9 | public class ReceiverOptions
10 | {
11 | public Uri ControllerUrl { get; set; }
12 |
13 | public HttpMessageInvoker Client { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Queues/ProcessingRateLimitedQueue.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Yarp.Kubernetes.Controller.Rate;
7 |
8 | namespace Yarp.Kubernetes.Controller.Queues;
9 |
10 | public class ProcessingRateLimitedQueue : WorkQueue
11 | {
12 | private readonly Limiter _limiter;
13 |
14 | public ProcessingRateLimitedQueue(double perSecond, int burst)
15 | {
16 | _limiter = new Limiter(new Limit(perSecond), burst);
17 | }
18 |
19 | protected override async Task OnGetAsync(CancellationToken cancellationToken)
20 | {
21 | var delay = _limiter.Reserve().Delay();
22 | await Task.Delay(delay, cancellationToken);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Services/IReconciler.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.Kubernetes.Controller.Services;
8 |
9 | ///
10 | /// IReconciler is a service interface called by the to process
11 | /// the work items as they are dequeued.
12 | ///
13 | public interface IReconciler
14 | {
15 | Task ProcessAsync(CancellationToken cancellationToken);
16 | }
17 |
--------------------------------------------------------------------------------
/src/Kubernetes.Controller/Yarp.Kubernetes.Controller.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Toolkit for building a Kubernetes Ingress Controller in .NET using the infrastructure from ASP.NET and .NET
5 | $(ReleaseTFMs)
6 | Library
7 | $(NoWarn);CS8002
8 | true
9 | false
10 | yarp;dotnet;reverse-proxy;aspnetcore;kubernetes
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/AuthorizationConstants.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Configuration;
5 |
6 | internal static class AuthorizationConstants
7 | {
8 | internal const string Default = "Default";
9 | internal const string Anonymous = "Anonymous";
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/ConfigProvider/ConfigurationSnapshot.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Microsoft.Extensions.Primitives;
6 |
7 | namespace Yarp.ReverseProxy.Configuration.ConfigProvider;
8 |
9 | internal sealed class ConfigurationSnapshot : IProxyConfig
10 | {
11 | public List Routes { get; internal set; } = new List();
12 |
13 | public List Clusters { get; internal set; } = new List();
14 |
15 | IReadOnlyList IProxyConfig.Routes => Routes;
16 |
17 | IReadOnlyList IProxyConfig.Clusters => Clusters;
18 |
19 | // This field is required.
20 | public IChangeToken ChangeToken { get; internal set; } = default!;
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/CorsConstants.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Configuration;
5 |
6 | internal static class CorsConstants
7 | {
8 | internal const string Default = "Default";
9 | internal const string Disable = "Disable";
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/IConfigValidator.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Threading.Tasks;
7 |
8 | namespace Yarp.ReverseProxy.Configuration;
9 |
10 | ///
11 | /// Provides methods to validate routes and clusters.
12 | ///
13 | public interface IConfigValidator
14 | {
15 | ///
16 | /// Validates a route and returns all errors
17 | ///
18 | ValueTask> ValidateRouteAsync(RouteConfig route);
19 |
20 | ///
21 | /// Validates a cluster and returns all errors.
22 | ///
23 | ValueTask> ValidateClusterAsync(ClusterConfig cluster);
24 | }
25 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/IProxyConfigProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Configuration;
5 |
6 | ///
7 | /// A data source for proxy route and cluster information.
8 | ///
9 | public interface IProxyConfigProvider
10 | {
11 | ///
12 | /// Returns the current route and cluster data.
13 | ///
14 | ///
15 | IProxyConfig GetConfig();
16 | }
17 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/InMemoryConfigProviderExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.ReverseProxy.Configuration;
6 |
7 | namespace Microsoft.Extensions.DependencyInjection;
8 |
9 | public static class InMemoryConfigProviderExtensions
10 | {
11 | ///
12 | /// Adds an InMemoryConfigProvider
13 | ///
14 | public static IReverseProxyBuilder LoadFromMemory(this IReverseProxyBuilder builder, IReadOnlyList routes, IReadOnlyList clusters)
15 | {
16 | builder.Services.AddSingleton(new InMemoryConfigProvider(routes, clusters));
17 | builder.Services.AddSingleton(s => s.GetRequiredService());
18 | return builder;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/RateLimitingConstants.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Configuration;
5 |
6 | internal static class RateLimitingConstants
7 | {
8 | internal const string Default = "Default";
9 | internal const string Disable = "Disable";
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Configuration/TimeoutPolicyConstants.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Configuration;
5 |
6 | internal static class TimeoutPolicyConstants
7 | {
8 | internal const string Disable = "Disable";
9 | }
10 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Delegation/DelegationExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Yarp.ReverseProxy.Model;
5 |
6 | namespace Yarp.ReverseProxy.Delegation;
7 |
8 | internal static class DelegationExtensions
9 | {
10 | public const string HttpSysDelegationQueueMetadataKey = "HttpSysDelegationQueue";
11 |
12 | public static string? GetHttpSysDelegationQueue(this DestinationState? destination)
13 | {
14 | return destination?.Model?.Config?.Metadata?.TryGetValue(HttpSysDelegationQueueMetadataKey, out var name) ?? false
15 | ? name
16 | : null;
17 | }
18 |
19 | public static bool ShouldUseHttpSysDelegation(this DestinationState destination)
20 | {
21 | return destination.GetHttpSysDelegationQueue() is not null;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Delegation/DummyHttpSysDelegator.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Delegation;
5 |
6 | // Only used as part of a workaround for https://github.com/dotnet/aspnetcore/issues/59166.
7 | internal sealed class DummyHttpSysDelegator : IHttpSysDelegator
8 | {
9 | public void ResetQueue(string queueName, string urlPrefix) { }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Delegation/IHttpSysDelegator.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Delegation;
5 |
6 | public interface IHttpSysDelegator
7 | {
8 | ///
9 | /// Disposes the handle to the given queue if it exists.
10 | ///
11 | ///
12 | /// If any destinations still reference the queue, the handle will be
13 | /// re-created the next time a request is routed to one of the destinations.
14 | ///
15 | /// The name of the queue to reset.
16 | /// The url prefix of the queue to reset.
17 | void ResetQueue(string queueName, string urlPrefix);
18 | }
19 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/DirectForwardingHttpClientProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Net.Http;
5 | using Yarp.ReverseProxy.Configuration;
6 |
7 | namespace Yarp.ReverseProxy.Forwarder;
8 |
9 | internal sealed class DirectForwardingHttpClientProvider
10 | {
11 | public HttpMessageInvoker HttpClient { get; }
12 |
13 | public DirectForwardingHttpClientProvider() : this(new ForwarderHttpClientFactory()) { }
14 |
15 | public DirectForwardingHttpClientProvider(IForwarderHttpClientFactory factory)
16 | {
17 | HttpClient = factory.CreateClient(new ForwarderHttpClientContext
18 | {
19 | NewConfig = HttpClientConfig.Empty
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/EmptyHttpContent.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.IO;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Yarp.ReverseProxy.Forwarder;
11 |
12 | internal sealed class EmptyHttpContent : HttpContent
13 | {
14 | protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) => Task.CompletedTask;
15 |
16 | protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) => Task.CompletedTask;
17 |
18 | protected override bool TryComputeLength(out long length)
19 | {
20 | length = 0;
21 | return true;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/ForwarderErrorFeature.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Forwarder;
7 |
8 | internal sealed class ForwarderErrorFeature : IForwarderErrorFeature
9 | {
10 | internal ForwarderErrorFeature(ForwarderError error, Exception? ex)
11 | {
12 | Error = error;
13 | Exception = ex;
14 | }
15 |
16 | ///
17 | /// The specified ForwarderError.
18 | ///
19 | public ForwarderError Error { get; }
20 |
21 | ///
22 | /// The error, if any.
23 | ///
24 | public Exception? Exception { get; }
25 | }
26 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/ForwarderStage.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Forwarder;
5 |
6 | internal enum ForwarderStage : int
7 | {
8 | SendAsyncStart = 1,
9 | SendAsyncStop,
10 | RequestContentTransferStart,
11 | ResponseContentTransferStart,
12 | ResponseUpgrade,
13 | }
14 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/IForwarderErrorFeature.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Forwarder;
7 |
8 | ///
9 | /// Stores errors and exceptions that occurred when forwarding the request to the destination.
10 | ///
11 | public interface IForwarderErrorFeature
12 | {
13 | ///
14 | /// The specified ProxyError.
15 | ///
16 | ForwarderError Error { get; }
17 |
18 | ///
19 | /// An Exception that occurred when forwarding the request to the destination, if any.
20 | ///
21 | Exception? Exception { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Forwarder/StreamCopyResult.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Forwarder;
5 |
6 | internal enum StreamCopyResult
7 | {
8 | Success,
9 | InputError,
10 | OutputError,
11 | Canceled
12 | }
13 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/ActiveHealthCheckMonitorOptions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Health;
7 |
8 | ///
9 | /// Defines options for the active health check monitor.
10 | ///
11 | public class ActiveHealthCheckMonitorOptions
12 | {
13 | ///
14 | /// Default probing interval.
15 | ///
16 | public TimeSpan DefaultInterval { get; set; } = TimeSpan.FromSeconds(15);
17 |
18 | ///
19 | /// Default probes timeout.
20 | ///
21 | public TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(10);
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/AppBuilderHealthExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Yarp.ReverseProxy.Health;
5 |
6 | namespace Microsoft.AspNetCore.Builder;
7 |
8 | ///
9 | /// Extensions for adding proxy middleware to the pipeline.
10 | ///
11 | public static class AppBuilderHealthExtensions
12 | {
13 | ///
14 | /// Passively checks destinations health by watching for successes and failures in client request proxying.
15 | ///
16 | public static IReverseProxyApplicationBuilder UsePassiveHealthChecks(this IReverseProxyApplicationBuilder builder)
17 | {
18 | builder.UseMiddleware();
19 | return builder;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/HealthyOrPanicDestinationsPolicy.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.ReverseProxy.Configuration;
6 | using Yarp.ReverseProxy.Model;
7 |
8 | namespace Yarp.ReverseProxy.Health;
9 |
10 | internal sealed class HealthyOrPanicDestinationsPolicy : HealthyAndUnknownDestinationsPolicy
11 | {
12 | public override string Name => HealthCheckConstants.AvailableDestinations.HealthyOrPanic;
13 |
14 | public override IReadOnlyList GetAvailableDestinations(ClusterConfig config, IReadOnlyList allDestinations)
15 | {
16 | var availableDestinations = base.GetAvailableDestinations(config, allDestinations);
17 | return availableDestinations.Count > 0 ? availableDestinations : allDestinations;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/IActiveHealthCheckPolicy.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.ReverseProxy.Model;
6 |
7 | namespace Yarp.ReverseProxy.Health;
8 |
9 | ///
10 | /// Active health check evaluation policy.
11 | ///
12 | public interface IActiveHealthCheckPolicy
13 | {
14 | ///
15 | /// Policy's name.
16 | ///
17 | string Name { get; }
18 |
19 | ///
20 | /// Analyzes results of active health probes sent to destinations and calculates their new health states.
21 | ///
22 | /// Cluster.
23 | /// Destination probing results.
24 | void ProbingCompleted(ClusterState cluster, IReadOnlyList probingResults);
25 | }
26 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/IProbingRequestFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Net.Http;
5 | using Yarp.ReverseProxy.Model;
6 |
7 | namespace Yarp.ReverseProxy.Health;
8 |
9 | ///
10 | /// A factory for creating s for active health probes to be sent to destinations.
11 | ///
12 | public interface IProbingRequestFactory
13 | {
14 | ///
15 | /// Creates a probing request.
16 | ///
17 | /// The cluster being probed.
18 | /// The destination being probed.
19 | /// Probing .
20 | HttpRequestMessage CreateRequest(ClusterModel cluster, DestinationModel destination);
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Health/NewActiveDestinationHealth.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Yarp.ReverseProxy.Model;
5 |
6 | namespace Yarp.ReverseProxy.Health;
7 |
8 | ///
9 | /// Stores a new active health state for the given destination.
10 | ///
11 | public readonly struct NewActiveDestinationHealth
12 | {
13 | public NewActiveDestinationHealth(DestinationState destination, DestinationHealth newActiveHealth)
14 | {
15 | Destination = destination;
16 | NewActiveHealth = newActiveHealth;
17 | }
18 |
19 | public DestinationState Destination { get; }
20 |
21 | public DestinationHealth NewActiveHealth { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/LoadBalancing/AppBuilderLoadBalancingExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Yarp.ReverseProxy.LoadBalancing;
5 |
6 | namespace Microsoft.AspNetCore.Builder;
7 |
8 | ///
9 | /// Extensions for adding proxy middleware to the pipeline.
10 | ///
11 | public static class AppBuilderLoadBalancingExtensions
12 | {
13 | ///
14 | /// Load balances across the available endpoints.
15 | ///
16 | public static IReverseProxyApplicationBuilder UseLoadBalancing(this IReverseProxyApplicationBuilder builder)
17 | {
18 | builder.UseMiddleware();
19 | return builder;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Management/IReverseProxyBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Microsoft.Extensions.DependencyInjection;
5 |
6 | ///
7 | /// Reverse Proxy builder interface.
8 | ///
9 | public interface IReverseProxyBuilder
10 | {
11 | ///
12 | /// Gets the services.
13 | ///
14 | IServiceCollection Services { get; }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Model/ClusterDestinationsState.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace Yarp.ReverseProxy.Model;
8 |
9 | public sealed class ClusterDestinationsState
10 | {
11 | public ClusterDestinationsState(
12 | IReadOnlyList allDestinations,
13 | IReadOnlyList availableDestinations)
14 | {
15 | AllDestinations = allDestinations ?? throw new ArgumentNullException(nameof(allDestinations));
16 | AvailableDestinations = availableDestinations ?? throw new ArgumentNullException(nameof(availableDestinations));
17 | }
18 |
19 | public IReadOnlyList AllDestinations { get; }
20 |
21 | public IReadOnlyList AvailableDestinations { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Model/DestinationHealth.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Model;
5 |
6 | public enum DestinationHealth
7 | {
8 | Unknown,
9 |
10 | Healthy,
11 |
12 | Unhealthy,
13 | }
14 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Model/DestinationHealthState.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Model;
5 |
6 | ///
7 | /// Tracks destination passive and active health states.
8 | ///
9 | public class DestinationHealthState
10 | {
11 | private volatile DestinationHealth _active;
12 | private volatile DestinationHealth _passive;
13 |
14 | ///
15 | /// Passive health state.
16 | ///
17 | public DestinationHealth Passive
18 | {
19 | get => _passive;
20 | set => _passive = value;
21 | }
22 |
23 | ///
24 | /// Active health state.
25 | ///
26 | public DestinationHealth Active
27 | {
28 | get => _active;
29 | set => _active = value;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Model/IReverseProxyApplicationBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Microsoft.AspNetCore.Builder;
5 |
6 | ///
7 | /// An for building the `MapReverseProxy` pipeline.
8 | ///
9 | public interface IReverseProxyApplicationBuilder : IApplicationBuilder
10 | {
11 | }
12 |
--------------------------------------------------------------------------------
/src/ReverseProxy/README.md:
--------------------------------------------------------------------------------
1 | YARP (Yet Another Reverse Proxy) is a highly customizable reverse proxy built using .NET. The biggest differentiator between YARP and other reverse proxies is how it is built and packaged – YARP is supplied as a library and samples showing how to create a proxy that is customized to the needs of your specific scenarios.
2 |
3 | To learn more see the docs at https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started, the GitHub repo at https://github.com/dotnet/yarp, and the 1.0 Announcement Blog post at https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release/.
4 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Routing/HeaderMetadata.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Yarp.ReverseProxy.Routing;
9 |
10 | ///
11 | /// Represents request header metadata used during routing.
12 | ///
13 | internal sealed class HeaderMetadata : IHeaderMetadata
14 | {
15 | public HeaderMetadata(IReadOnlyList matchers)
16 | {
17 | Matchers = matchers?.ToArray() ?? throw new ArgumentNullException(nameof(matchers));
18 | }
19 |
20 | ///
21 | public HeaderMatcher[] Matchers { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Routing/IHeaderMetadata.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Routing;
5 |
6 | ///
7 | /// Represents request header metadata used during routing.
8 | ///
9 | internal interface IHeaderMetadata
10 | {
11 | ///
12 | /// One or more matchers to apply to the request headers.
13 | ///
14 | HeaderMatcher[] Matchers { get; }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Routing/IQueryParameterMetadata.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Routing;
5 |
6 | ///
7 | /// Represents request query parameter metadata used during routing.
8 | ///
9 | internal interface IQueryParameterMetadata
10 | {
11 | ///
12 | /// One or more matchers to apply to the request query parameters.
13 | ///
14 | QueryParameterMatcher[] Matchers { get; }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Routing/QueryParameterMetadata.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Yarp.ReverseProxy.Routing;
9 |
10 | ///
11 | /// Represents request query parameter metadata used during routing.
12 | ///
13 | internal sealed class QueryParameterMetadata : IQueryParameterMetadata
14 | {
15 | public QueryParameterMetadata(IReadOnlyList matchers)
16 | {
17 | Matchers = matchers?.ToArray() ?? throw new ArgumentNullException(nameof(matchers));
18 | }
19 |
20 | ///
21 | public QueryParameterMatcher[] Matchers { get; }
22 | }
23 |
--------------------------------------------------------------------------------
/src/ReverseProxy/ServiceDiscovery/NoOpDestinationResolver.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Yarp.ReverseProxy.Configuration;
8 |
9 | namespace Yarp.ReverseProxy.ServiceDiscovery;
10 |
11 | ///
12 | /// An which performs no action.
13 | ///
14 | internal sealed class NoOpDestinationResolver : IDestinationResolver
15 | {
16 | public ValueTask ResolveDestinationsAsync(IReadOnlyDictionary destinations, CancellationToken cancellationToken)
17 | => new(new ResolvedDestinationCollection(destinations, changeToken: null));
18 | }
19 |
--------------------------------------------------------------------------------
/src/ReverseProxy/SessionAffinity/AffinityResult.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Yarp.ReverseProxy.Model;
6 |
7 | namespace Yarp.ReverseProxy.SessionAffinity;
8 |
9 | ///
10 | /// Affinity resolution result.
11 | ///
12 | public readonly struct AffinityResult
13 | {
14 | public IReadOnlyList? Destinations { get; }
15 |
16 | public AffinityStatus Status { get; }
17 |
18 | public AffinityResult(IReadOnlyList? destinations, AffinityStatus status)
19 | {
20 | Destinations = destinations;
21 | Status = status;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ReverseProxy/SessionAffinity/AffinityStatus.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.SessionAffinity;
5 |
6 | ///
7 | /// Affinity resolution status.
8 | ///
9 | public enum AffinityStatus
10 | {
11 | OK,
12 | AffinityKeyNotSet,
13 | AffinityKeyExtractionFailed,
14 | DestinationNotFound
15 | }
16 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/ForwardedTransformActions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Transforms;
5 |
6 | public enum ForwardedTransformActions
7 | {
8 | Off = 0,
9 | Set,
10 | Append,
11 | Remove
12 | }
13 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/NodeFormat.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Transforms;
5 |
6 | ///
7 | /// For use with .
8 | ///
9 | public enum NodeFormat
10 | {
11 | None,
12 | Random,
13 | RandomAndPort,
14 | RandomAndRandomPort,
15 | Unknown,
16 | UnknownAndPort,
17 | UnknownAndRandomPort,
18 | Ip,
19 | IpAndPort,
20 | IpAndRandomPort,
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/RequestFuncTransform.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.ReverseProxy.Transforms;
8 |
9 | ///
10 | /// A request transform that runs the given Func.
11 | ///
12 | public class RequestFuncTransform : RequestTransform
13 | {
14 | private readonly Func _func;
15 |
16 | public RequestFuncTransform(Func func)
17 | {
18 | _func = func ?? throw new ArgumentNullException(nameof(func));
19 | }
20 |
21 | ///
22 | public override ValueTask ApplyAsync(RequestTransformContext context)
23 | {
24 | return _func(context);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/ResponseCondition.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.Transforms;
5 |
6 | ///
7 | /// Specifies the conditions under which a response transform will run.
8 | ///
9 | public enum ResponseCondition
10 | {
11 | ///
12 | /// The transform runs for all conditions.
13 | ///
14 | Always,
15 |
16 | ///
17 | /// The transform only runs if there is a successful response with a status code less than 400.
18 | ///
19 | Success,
20 |
21 | ///
22 | /// The transform only runs if there is no response or a response with a 400+ status code.
23 | ///
24 | Failure
25 | }
26 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/ResponseFuncTransform.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.ReverseProxy.Transforms;
8 |
9 | ///
10 | /// A response transform that runs the given Func.
11 | ///
12 | public class ResponseFuncTransform : ResponseTransform
13 | {
14 | private readonly Func _func;
15 |
16 | public ResponseFuncTransform(Func func)
17 | {
18 | _func = func ?? throw new ArgumentNullException(nameof(func));
19 | }
20 |
21 | ///
22 | public override ValueTask ApplyAsync(ResponseTransformContext context)
23 | {
24 | return _func(context);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Transforms/ResponseTrailersFuncTransform.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.ReverseProxy.Transforms;
8 |
9 | ///
10 | /// A response trailers transform that runs the given Func.
11 | ///
12 | public class ResponseTrailersFuncTransform : ResponseTrailersTransform
13 | {
14 | private readonly Func _func;
15 |
16 | public ResponseTrailersFuncTransform(Func func)
17 | {
18 | _func = func ?? throw new ArgumentNullException(nameof(func));
19 | }
20 |
21 | ///
22 | public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
23 | {
24 | return _func(context);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/CaseInsensitiveEqualHelper.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace Yarp.ReverseProxy.Utilities;
8 |
9 | internal static class CaseInsensitiveEqualHelper
10 | {
11 | internal static bool Equals(IReadOnlyList? list1, IReadOnlyList? list2)
12 | {
13 | return CollectionEqualityHelper.Equals(list1, list2, StringComparer.OrdinalIgnoreCase);
14 | }
15 |
16 | internal static int GetHashCode(IReadOnlyList? values)
17 | {
18 | return CollectionEqualityHelper.GetHashCode(values, StringComparer.OrdinalIgnoreCase);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/ConcurrentDictionaryExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 |
7 | namespace Yarp.ReverseProxy.Utilities;
8 |
9 | internal static class ConcurrentDictionaryExtensions
10 | {
11 | public static bool Contains(this ConcurrentDictionary dictionary, KeyValuePair item)
12 | where TKey : notnull
13 | {
14 | return ((ICollection>)dictionary).Contains(item);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/IRandomFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Utilities;
7 |
8 | ///
9 | /// Factory for creating random class. This factory let us able to inject random class into other class.
10 | /// So that we can mock the random class for unit test.
11 | ///
12 | public interface IRandomFactory
13 | {
14 | ///
15 | /// Create a instance of random class.
16 | ///
17 | Random CreateRandomInstance();
18 | }
19 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/NullRandomFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Utilities;
7 |
8 | internal sealed class NullRandomFactory : IRandomFactory
9 | {
10 | public Random CreateRandomInstance()
11 | {
12 | throw new NotImplementedException();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/RandomFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.ReverseProxy.Utilities;
7 |
8 | ///
9 | internal sealed class RandomFactory : IRandomFactory
10 | {
11 | ///
12 | public Random CreateRandomInstance()
13 | {
14 | return Random.Shared;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/SkipLocalsInit.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | // Used to indicate to the compiler that the .locals init flag should not be set in method headers.
5 | [module: System.Runtime.CompilerServices.SkipLocalsInit]
6 |
--------------------------------------------------------------------------------
/src/ReverseProxy/Utilities/TaskUtilities.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading.Tasks;
5 |
6 | namespace Yarp.ReverseProxy.Utilities;
7 |
8 | internal static class TaskUtilities
9 | {
10 | internal static readonly Task TrueTask = Task.FromResult(true);
11 | internal static readonly Task FalseTask = Task.FromResult(false);
12 | }
13 |
--------------------------------------------------------------------------------
/src/ReverseProxy/WebSocketsTelemetry/WebSocketCloseReason.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.ReverseProxy.WebSocketsTelemetry;
5 |
6 | internal enum WebSocketCloseReason : int
7 | {
8 | Unknown,
9 | ClientGracefulClose,
10 | ServerGracefulClose,
11 | ClientDisconnect,
12 | ServerDisconnect,
13 | ActivityTimeout,
14 | }
15 |
--------------------------------------------------------------------------------
/src/TelemetryConsumption/Forwarder/ForwarderStage.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.Telemetry.Consumption;
5 |
6 | ///
7 | /// Stages of forwarding a request.
8 | ///
9 | public enum ForwarderStage : int
10 | {
11 | SendAsyncStart = 1,
12 | SendAsyncStop,
13 | RequestContentTransferStart,
14 | ResponseContentTransferStart,
15 | ResponseUpgrade,
16 | }
17 |
--------------------------------------------------------------------------------
/src/TelemetryConsumption/IMetricsConsumer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.Telemetry.Consumption;
5 |
6 | ///
7 | /// A consumer of .
8 | ///
9 | public interface IMetricsConsumer
10 | {
11 | ///
12 | /// Processes from the last event counter interval.
13 | ///
14 | /// collected in the previous interval.
15 | /// collected in the last interval.
16 | void OnMetrics(TMetrics previous, TMetrics current);
17 | }
18 |
--------------------------------------------------------------------------------
/src/TelemetryConsumption/MetricsOptions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.Telemetry.Consumption;
7 |
8 | internal static class MetricsOptions
9 | {
10 | // TODO: Should this be publicly configurable? It's currently only visible to tests to reduce execution time
11 | public static TimeSpan Interval { get; set; } = TimeSpan.FromSeconds(1);
12 | }
13 |
--------------------------------------------------------------------------------
/src/TelemetryConsumption/README.md:
--------------------------------------------------------------------------------
1 | YARP (Yet Another Reverse Proxy) is a highly customizable reverse proxy built using .NET. This package extends the base Yarp.ReverseProxy implementation to enable consuming AspNetCore, HttpClient, and YARP telemetry in process, allowing you to live monitor the performance and export the data as needed.
2 |
3 | To learn more see the docs at https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/diagnosing-yarp-issues#using-telemetry-events and the GitHub repo at https://github.com/dotnet/yarp.
4 |
--------------------------------------------------------------------------------
/src/TelemetryConsumption/WebSockets/WebSocketCloseReason.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.Telemetry.Consumption;
5 |
6 | ///
7 | /// The reason the WebSocket connection closed.
8 | ///
9 | public enum WebSocketCloseReason : int
10 | {
11 | Unknown,
12 | ClientGracefulClose,
13 | ServerGracefulClose,
14 | ClientDisconnect,
15 | ServerDisconnect,
16 | ActivityTimeout,
17 | }
18 |
--------------------------------------------------------------------------------
/startvs.cmd:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | SETLOCAL
3 |
4 | :: This command launches a Visual Studio solution with environment variables required to use a local version of the .NET Core SDK.
5 |
6 | :: This tells .NET Core to use the same dotnet.exe that build scripts use
7 | SET DOTNET_ROOT=%~dp0.dotnet
8 | SET DOTNET_ROOT(x86)=%~dp0.dotnet\x86
9 |
10 | :: This tells .NET Core not to go looking for .NET Core in other places
11 | SET DOTNET_MULTILEVEL_LOOKUP=0
12 |
13 | :: Put our local dotnet.exe on PATH first so Visual Studio knows which one to use
14 | SET PATH=%DOTNET_ROOT%;%PATH%
15 |
16 | SET sln=%~1
17 |
18 | IF "%sln%"=="" (
19 | echo Solution not specified, using YARP.slnx
20 | SET sln=%~dp0YARP.slnx
21 | )
22 |
23 | IF NOT EXIST "%DOTNET_ROOT%\dotnet.exe" (
24 | echo .NET Core has not yet been installed. Run `%~dp0restore.cmd` to install tools
25 | exit /b 1
26 | )
27 |
28 | start "" "%sln%"
29 |
--------------------------------------------------------------------------------
/test.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -test %*"
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source="${BASH_SOURCE[0]}"
4 |
5 | # resolve $SOURCE until the file is no longer a symlink
6 | while [[ -h $source ]]; do
7 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
8 | source="$(readlink "$source")"
9 |
10 | # if $source was a relative symlink, we need to resolve it relative to the path where the
11 | # symlink file was located
12 | [[ $source != /* ]] && source="$scriptroot/$source"
13 | done
14 |
15 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
16 | "$scriptroot/eng/common/build.sh" --test $@
--------------------------------------------------------------------------------
/test/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | true
8 |
9 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/Hosting/Fakes/FakeServer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Hosting.Server;
5 | using Microsoft.AspNetCore.Http.Features;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Yarp.Kubernetes.Tests.Hosting.Fakes;
10 |
11 | public sealed class FakeServer : IServer
12 | {
13 | public IFeatureCollection Features { get; } = new FeatureCollection();
14 |
15 | public void Dispose()
16 | {
17 | }
18 |
19 | public Task StartAsync(IHttpApplication application, CancellationToken cancellationToken)
20 | {
21 | return Task.CompletedTask;
22 | }
23 |
24 | public Task StopAsync(CancellationToken cancellationToken)
25 | {
26 | return Task.CompletedTask;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/Hosting/Fakes/TestLatches.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | namespace Yarp.Kubernetes.Tests.Hosting.Fakes;
5 |
6 | public class TestLatches
7 | {
8 | public TestLatch RunEnter { get; } = new TestLatch();
9 | public TestLatch RunResult { get; } = new TestLatch();
10 | public TestLatch RunExit { get; } = new TestLatch();
11 | }
12 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/ITestCluster.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Http;
5 | using System.Threading.Tasks;
6 | using Yarp.Kubernetes.Tests.TestCluster.Models;
7 |
8 | namespace Yarp.Kubernetes.Tests.TestCluster;
9 |
10 | public interface ITestCluster
11 | {
12 | Task UnhandledRequest(HttpContext context);
13 |
14 | Task ListResourcesAsync(string group, string version, string plural, ListParameters parameters);
15 | }
16 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/ITestClusterHost.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s;
5 | using k8s.KubeConfigModels;
6 | using Microsoft.Extensions.Hosting;
7 |
8 | namespace Yarp.Kubernetes.Tests.TestCluster;
9 |
10 | public interface ITestClusterHost : IHost
11 | {
12 | K8SConfiguration KubeConfig { get; }
13 |
14 | IKubernetes Client { get; }
15 |
16 | ITestCluster Cluster { get; }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/Models/ListParameters.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace Yarp.Kubernetes.Tests.TestCluster.Models;
7 |
8 | public class ListParameters
9 | {
10 | [FromQuery]
11 | public string Continue { get; set; }
12 |
13 | [FromQuery]
14 | public string FieldSelector { get; set; }
15 |
16 | [FromQuery]
17 | public string LabelSelector { get; set; }
18 |
19 | [FromQuery]
20 | public int? Limit { get; set; }
21 |
22 | [FromQuery]
23 | public string ResourceVersion { get; set; }
24 |
25 | [FromQuery]
26 | public int? TimeoutSeconds { get; set; }
27 |
28 | [FromQuery]
29 | public bool? Watch { get; set; }
30 |
31 | [FromQuery]
32 | public string Pretty { get; set; }
33 | }
34 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/Models/ListResult.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 |
6 | namespace Yarp.Kubernetes.Tests.TestCluster.Models;
7 |
8 | public class ListResult
9 | {
10 | public string Continue { get; set; }
11 |
12 | public string ResourceVersion { get; set; }
13 |
14 | public IList Items { get; set; }
15 | }
16 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/Models/ResourceObject.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s;
5 | using k8s.Models;
6 | using System.Collections.Generic;
7 | using System.Text.Json.Serialization;
8 |
9 | namespace Yarp.Kubernetes.Tests.TestCluster.Models;
10 |
11 | public class ResourceObject : IKubernetesObject
12 | {
13 | [JsonPropertyName("apiVersion")]
14 | public string ApiVersion { get; set; }
15 |
16 | [JsonPropertyName("kind")]
17 | public string Kind { get; set; }
18 |
19 | [JsonPropertyName("metadata")]
20 | public V1ObjectMeta Metadata { get; set; }
21 |
22 | [JsonExtensionData]
23 | public IDictionary AdditionalData { get; set; }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/TestCluster/TestClusterOptions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using k8s;
5 | using k8s.Models;
6 | using System.Collections.Generic;
7 |
8 | namespace Yarp.Kubernetes.Tests.TestCluster;
9 |
10 | public class TestClusterOptions
11 | {
12 | public IList> InitialResources { get; } = new List>();
13 | }
14 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/annotations/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null
9 | },
10 | "Order": null,
11 | "ClusterId": "frontend.default:80",
12 | "AuthorizationPolicy": "authzpolicy",
13 | "RateLimiterPolicy": "ratelimiterpolicy",
14 | "CorsPolicy": "corspolicy",
15 | "Metadata": null,
16 | "Transforms": [
17 | { "PathPrefix": "/apis" },
18 | {
19 | "RequestHeader": "header1",
20 | "Append": "bar"
21 | }
22 | ]
23 | }
24 | ]
25 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/basic-ingress/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/basic-ingress/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: minimal-ingress
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: frontend
15 | port:
16 | number: 80
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: frontend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: frontend
26 | ports:
27 | - name: http
28 | port: 80
29 | targetPort: 80
30 | type: ClusterIP
31 | ---
32 | apiVersion: v1
33 | kind: Endpoints
34 | metadata:
35 | name: frontend
36 | namespace: default
37 | subsets:
38 | - addresses:
39 | - ip: 10.244.2.38
40 | ports:
41 | - name: http
42 | port: 80
43 | protocol: TCP
44 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/basic-ingress/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/exact-match/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/exact-match/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: minimal-ingress
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: ExactMatch
12 | backend:
13 | service:
14 | name: frontend
15 | port:
16 | number: 80
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: frontend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: frontend
26 | ports:
27 | - name: http
28 | port: 80
29 | targetPort: 80
30 | type: ClusterIP
31 | ---
32 | apiVersion: v1
33 | kind: Endpoints
34 | metadata:
35 | name: frontend
36 | namespace: default
37 | subsets:
38 | - addresses:
39 | - ip: 10.244.2.38
40 | ports:
41 | - name: http
42 | port: 80
43 | protocol: TCP
44 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/exact-match/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/external-name-ingress/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "external-service.default:443",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "https://external-service.example.com:443": {
11 | "Address": "https://external-service.example.com:443",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/external-name-ingress/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: external-service
5 | namespace: default
6 | spec:
7 | type: ExternalName
8 | externalName: external-service.example.com
9 | ---
10 | apiVersion: networking.k8s.io/v1
11 | kind: Ingress
12 | metadata:
13 | name: external-ingress
14 | namespace: default
15 | spec:
16 | rules:
17 | - http:
18 | paths:
19 | - path: /foo
20 | pathType: Prefix
21 | backend:
22 | service:
23 | name: external-service
24 | port:
25 | number: 443
26 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/external-name-ingress/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "external-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "external-service.default:443",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/hostname-routing/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.foo:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/hostname-routing/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: hostname-routing
5 | namespace: foo
6 | spec:
7 | rules:
8 | - host: foo.bar.com
9 | http:
10 | paths:
11 | - path: /
12 | pathType: Prefix
13 | backend:
14 | service:
15 | name: frontend
16 | port:
17 | number: 80
18 | ---
19 | apiVersion: v1
20 | kind: Service
21 | metadata:
22 | name: frontend
23 | namespace: foo
24 | spec:
25 | selector:
26 | app: frontend
27 | ports:
28 | - name: http
29 | port: 80
30 | targetPort: 80
31 | type: ClusterIP
32 | ---
33 | apiVersion: v1
34 | kind: Endpoints
35 | metadata:
36 | name: frontend
37 | namespace: foo
38 | subsets:
39 | - addresses:
40 | - ip: 10.244.2.38
41 | ports:
42 | - name: http
43 | port: 80
44 | protocol: TCP
45 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/hostname-routing/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "hostname-routing.foo:foo.bar.com/",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [ "foo.bar.com" ],
7 | "Path": "/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.foo:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/https/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:443",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "https://10.244.2.38:443": {
11 | "Address": "https://10.244.2.38:443",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/https/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:443",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/mapped-port/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "backend.default:5011",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.33:80": {
11 | "Address": "http://10.244.2.33:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/mapped-port/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: mapped-port
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: backend
15 | port:
16 | number: 5011
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: backend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: backend
26 | ports:
27 | - port: 5011
28 | targetPort: 80
29 | type: ClusterIP
30 | ---
31 | apiVersion: v1
32 | kind: Endpoints
33 | metadata:
34 | name: backend
35 | namespace: default
36 | subsets:
37 | - addresses:
38 | - ip: 10.244.2.33
39 | ports:
40 | - port: 80
41 | protocol: TCP
42 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/mapped-port/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "mapped-port.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "backend.default:5011",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/missing-svc/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/missing-svc/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: missing-svc
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: backend
15 | port:
16 | number: 80
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: frontend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: frontend
26 | ports:
27 | - name: http
28 | port: 80
29 | targetPort: 80
30 | type: ClusterIP
31 | ---
32 | apiVersion: v1
33 | kind: Endpoints
34 | metadata:
35 | name: frontend
36 | namespace: default
37 | subsets:
38 | - addresses:
39 | - ip: 10.244.2.38
40 | ports:
41 | - name: http
42 | port: 80
43 | protocol: TCP
44 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/missing-svc/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/multiple-endpoints-ports/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/multiple-endpoints-ports/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/multiple-endpoints-same-port/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/multiple-endpoints-same-port/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/multiple-ingresses-one-svc/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "repro-service.foo:http",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.1.11:80": {
11 | "Address": "http://10.244.1.11:80",
12 | "Health": null,
13 | "Metadata": null
14 | },
15 | "http://10.244.1.12:80": {
16 | "Address": "http://10.244.1.12:80",
17 | "Health": null,
18 | "Metadata": null
19 | }
20 | },
21 | "Metadata": null
22 | }
23 | ]
24 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-diff-name/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "backend.default:88",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.33:80": {
11 | "Address": "http://10.244.2.33:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-diff-name/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: port-diff-name
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: backend
15 | port:
16 | number: 88
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: backend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: backend
26 | ports:
27 | - port: 88
28 | name: my-http
29 | targetPort: http
30 | type: ClusterIP
31 | ---
32 | apiVersion: v1
33 | kind: Endpoints
34 | metadata:
35 | name: backend
36 | namespace: default
37 | subsets:
38 | - addresses:
39 | - ip: 10.244.2.33
40 | ports:
41 | - port: 80
42 | protocol: TCP
43 | name: my-http
44 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-diff-name/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "port-diff-name.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "backend.default:88",
13 | "AuthorizationPolicy": null,
14 | "CorsPolicy": null,
15 | "Metadata": null,
16 | "Transforms": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-mismatch/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
4 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-mismatch/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: port-mismatch
5 | namespace: default
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /foo
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: backend
15 | port:
16 | number: 5011
17 | ---
18 | apiVersion: v1
19 | kind: Service
20 | metadata:
21 | name: backend
22 | namespace: default
23 | spec:
24 | selector:
25 | app: backend
26 | ports:
27 | - port: 80
28 | targetPort: 80
29 | type: ClusterIP
30 | ---
31 | apiVersion: v1
32 | kind: Endpoints
33 | metadata:
34 | name: backend
35 | namespace: default
36 | subsets:
37 | - addresses:
38 | - ip: 10.244.2.33
39 | ports:
40 | - port: 80
41 | protocol: TCP
42 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/port-mismatch/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/resource-informer/ResourcesAreListedWhenReadyAsyncIsComplete/resources.yaml:
--------------------------------------------------------------------------------
1 | # pods
2 | - apiVersion: v1
3 | kind: Pod
4 | metadata:
5 | name: pod-1
6 | namespace: the-namespace
7 | spec:
8 | containers:
9 | - name: test
10 | image: test
11 | - apiVersion: v1
12 | kind: Pod
13 | metadata:
14 | name: pod-2
15 | namespace: the-namespace
16 | spec:
17 | containers:
18 | - name: test
19 | image: test
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/resource-informer/ResourcesAreListedWhenReadyAsyncIsComplete/shouldbe.yaml:
--------------------------------------------------------------------------------
1 | - namespace: the-namespace
2 | name: pod-1
3 | - namespace: the-namespace
4 | name: pod-2
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/resource-informer/ResourcesWithApiGroupAreListed/resources.yaml:
--------------------------------------------------------------------------------
1 | # pods
2 | - apiVersion: apps/v1
3 | kind: Deployment
4 | metadata:
5 | name: deployment-1
6 | namespace: the-namespace
7 | spec:
8 | template:
9 | spec:
10 | containers:
11 | - name: test
12 | image: test
13 | - apiVersion: apps/v1
14 | kind: Deployment
15 | metadata:
16 | name: deployment-2
17 | namespace: the-namespace
18 | spec:
19 | template:
20 | spec:
21 | containers:
22 | - name: test
23 | image: test
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/resource-informer/ResourcesWithApiGroupAreListed/shouldbe.yaml:
--------------------------------------------------------------------------------
1 | - namespace: the-namespace
2 | name: deployment-1
3 | - namespace: the-namespace
4 | name: deployment-2
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-headers/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-headers/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": [
9 | {
10 | "Name": "the-header-key",
11 | "Values": [ "the-header-value" ],
12 | "Mode": "Contains",
13 | "IsCaseSensitive": false
14 | }
15 | ],
16 | "QueryParameters": null
17 | },
18 | "Order": null,
19 | "ClusterId": "frontend.default:80",
20 | "AuthorizationPolicy": null,
21 | "RateLimiterPolicy": null,
22 | "CorsPolicy": null,
23 | "Metadata": {
24 | "foo": "bar",
25 | "another-key": "another-value"
26 | },
27 | "Transforms": null
28 | }
29 | ]
30 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-metadata/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-metadata/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": null,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": {
17 | "foo": "bar",
18 | "another-key": "another-value"
19 | },
20 | "Transforms": null
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-methods/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-order/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-order/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": null
10 | },
11 | "Order": 10,
12 | "ClusterId": "frontend.default:80",
13 | "AuthorizationPolicy": null,
14 | "RateLimiterPolicy": null,
15 | "CorsPolicy": null,
16 | "Metadata": null,
17 | "Transforms": null
18 | }
19 | ]
20 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-queryparameters/clusters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ClusterId": "frontend.default:80",
4 | "LoadBalancingPolicy": null,
5 | "SessionAffinity": null,
6 | "HealthCheck": null,
7 | "HttpClient": null,
8 | "HttpRequest": null,
9 | "Destinations": {
10 | "http://10.244.2.38:80": {
11 | "Address": "http://10.244.2.38:80",
12 | "Health": null,
13 | "Metadata": null
14 | }
15 | },
16 | "Metadata": null
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/test/Kubernetes.Tests/testassets/route-queryparameters/routes.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "RouteId": "minimal-ingress.default:/foo",
4 | "Match": {
5 | "Methods": null,
6 | "Hosts": [],
7 | "Path": "/foo/{**catch-all}",
8 | "Headers": null,
9 | "QueryParameters": [
10 | {
11 | "Name": "the-queryparameter-key",
12 | "Values": [ "the-queryparameter-value" ],
13 | "Mode": "Contains",
14 | "IsCaseSensitive": false
15 | }
16 | ]
17 | },
18 | "Order": null,
19 | "ClusterId": "frontend.default:80",
20 | "AuthorizationPolicy": null,
21 | "RateLimiterPolicy": null,
22 | "CorsPolicy": null,
23 | "Metadata": {
24 | "foo": "bar",
25 | "another-key": "another-value"
26 | },
27 | "Transforms": null
28 | }
29 | ]
30 |
--------------------------------------------------------------------------------
/test/ReverseProxy.FunctionalTests/Common/Helpers.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Hosting.Server;
5 | using Microsoft.AspNetCore.Hosting.Server.Features;
6 | using Microsoft.Extensions.Hosting;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using System.Linq;
9 |
10 | namespace Yarp.ReverseProxy;
11 |
12 | public static class Helpers
13 | {
14 | public static string GetAddress(this IHost server)
15 | {
16 | return server.Services.GetService().Features.Get().Addresses.First();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/ReverseProxy.FunctionalTests/TelemetryEnumTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using System.Linq;
6 | using Xunit;
7 |
8 | namespace Yarp.ReverseProxy;
9 |
10 | public class TelemetryEnumTests
11 | {
12 | [Theory]
13 | [InlineData(typeof(Telemetry.Consumption.ForwarderStage), typeof(Forwarder.ForwarderStage))]
14 | [InlineData(typeof(Telemetry.Consumption.WebSocketCloseReason), typeof(WebSocketsTelemetry.WebSocketCloseReason))]
15 | public void ExposedEnumsMatchInternalCopies(Type publicEnum, Type internalEnum)
16 | {
17 | Assert.Equal(internalEnum.GetEnumNames(), publicEnum.GetEnumNames());
18 | Assert.Equal(internalEnum.GetEnumValues().Cast().ToArray(), publicEnum.GetEnumValues().Cast().ToArray());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/ReverseProxy.Tests/Common/HttpContentExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.IO;
5 | using System.Net.Http;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Yarp.Tests.Common;
10 |
11 | internal static class HttpContentExtensions
12 | {
13 | public static Task CopyToWithCancellationAsync(this HttpContent httpContent, Stream stream)
14 | {
15 | // StreamCopyHttpContent assumes that the cancellation token passed to it can always be canceled.
16 | // This is the case for real callers, so we insert a dummy CTS in tests to allow us to keep the debug assertion.
17 | return httpContent.CopyToAsync(stream, new CancellationTokenSource().Token);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/ReverseProxy.Tests/Common/TestTrailersFeature.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Http.Features;
5 | using Microsoft.AspNetCore.Http;
6 |
7 | namespace Yarp.Tests.Common;
8 |
9 | internal sealed class TestTrailersFeature : IHttpResponseTrailersFeature
10 | {
11 | public IHeaderDictionary Trailers { get; set; } = new HeaderDictionary();
12 | }
13 |
--------------------------------------------------------------------------------
/test/ReverseProxy.Tests/Configuration/ConfigProvider/ConfigurationReadingExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Collections.Generic;
5 | using Xunit;
6 |
7 | namespace Microsoft.Extensions.Configuration.Tests;
8 |
9 | public class ConfigurationReadingExtensionsTests
10 | {
11 | [Fact]
12 | public void ReadInt32_NegativeNumber()
13 | {
14 | var configuration = new ConfigurationBuilder()
15 | .AddInMemoryCollection(new Dictionary
16 | {
17 | ["Key"] = "-1"
18 | })
19 | .Build();
20 |
21 | var number = configuration.ReadInt32("Key");
22 |
23 | Assert.Equal(-1, number);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/ReverseProxy.Tests/Utilities/RandomFactoryTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Xunit;
5 |
6 | namespace Yarp.ReverseProxy.Utilities.Tests;
7 |
8 | public class RandomFactoryTests
9 | {
10 | [Fact]
11 | public void RandomFactory_Work()
12 | {
13 | // Set up the factory.
14 | var factory = new RandomFactory();
15 |
16 | // Create random class object.
17 | var random = factory.CreateRandomInstance();
18 |
19 | // Validate.
20 | Assert.NotNull(random);
21 |
22 | // Validate functionality
23 | var num = random.Next(5);
24 | Assert.InRange(num, 0, 5);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/ReverseProxy.Tests/validSelfSignedClientEkuCertificate.cer:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/test/ReverseProxy.Tests/validSelfSignedClientEkuCertificate.cer
--------------------------------------------------------------------------------
/test/TestCertificates/testCert.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/test/TestCertificates/testCert.pfx
--------------------------------------------------------------------------------
/test/Tests.Common/TestLoggerProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.Extensions.Logging;
5 | using Microsoft.Extensions.Logging.Testing;
6 | using Xunit.Abstractions;
7 |
8 | namespace Yarp.ReverseProxy.Common;
9 |
10 | public sealed class TestLoggerProvider(ITestOutputHelper output) : ILoggerProvider
11 | {
12 | private readonly XunitLoggerProvider _xunitLoggerProvider = new(output);
13 |
14 | public ILogger CreateLogger(string categoryName) => new TestLogger(_xunitLoggerProvider.CreateLogger(categoryName), categoryName);
15 |
16 | public void Dispose() => _xunitLoggerProvider.Dispose();
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tests.Common/TestRandom.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 |
6 | namespace Yarp.Tests.Common;
7 |
8 | public class TestRandom : Random
9 | {
10 | public int[] Sequence { get; set; }
11 | public int Offset { get; set; }
12 |
13 | public override int Next(int maxValue)
14 | {
15 | return Sequence[Offset++];
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tests.Common/TestRandomFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using Yarp.ReverseProxy.Utilities;
6 |
7 | namespace Yarp.Tests.Common;
8 |
9 | public class TestRandomFactory : IRandomFactory
10 | {
11 | public TestRandom Instance { get; set; }
12 |
13 | public Random CreateRandomInstance()
14 | {
15 | return Instance;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tests.Common/Yarp.Tests.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(TestTFMs)
5 | Library
6 | Yarp.Common.Tests
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/testassets/BenchmarkApp/BenchmarkApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(TestTFMs)
5 | $(NoWarn);SYSLIB0057
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Always
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/testassets/BenchmarkApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "BenchmarkApp": {
4 | "commandName": "Project",
5 | "commandLineArgs": "--urls http://localhost:5000 --clusterUrls http://httpbin.org",
6 | "environmentVariables": {
7 | "ASPNETCORE_ENVIRONMENT": "Development"
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/testassets/BenchmarkApp/README.md:
--------------------------------------------------------------------------------
1 | ### Crank command to test against a local BenchmarkServer
2 |
3 | 1. Follow the [Crank Getting Started Guide](https://github.com/dotnet/crank/blob/master/docs/getting_started.md) to install Microsoft.Crank.Controller and Microsoft.Crank.Agent globally.
4 | 2. In one shell, run `crank-agent`
5 | 3. In another shell, run `crank` as follows:
6 |
7 | ```
8 | crank `
9 | --config https://raw.githubusercontent.com/aspnet/Benchmarks/master/scenarios/proxy.benchmarks.yml `
10 | --scenario proxy-yarp `
11 | --profile local `
12 | --load.variables.duration 5 `
13 | --variable path=/?s=1024 `
14 | --variable serverScheme=https `
15 | --variable downstreamScheme=https `
16 | --load.variables.transport http2 `
17 | --downstream.variables.httpProtocol http2
18 | ```
19 |
--------------------------------------------------------------------------------
/testassets/BenchmarkApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AllowedHosts": "*",
3 | "LogLevel": ""
4 | }
5 |
--------------------------------------------------------------------------------
/testassets/BenchmarkApp/testCert.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/testassets/BenchmarkApp/testCert.pfx
--------------------------------------------------------------------------------
/testassets/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | true
8 | false
9 |
10 |
11 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/ForwarderMetricsConsumer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System;
5 | using Yarp.Telemetry.Consumption;
6 |
7 | namespace Yarp.ReverseProxy.Sample;
8 |
9 | public sealed class ForwarderMetricsConsumer : IMetricsConsumer
10 | {
11 | public void OnMetrics(ForwarderMetrics previous, ForwarderMetrics current)
12 | {
13 | var elapsed = current.Timestamp - previous.Timestamp;
14 | var newRequests = current.RequestsStarted - previous.RequestsStarted;
15 | Console.Title = $"Proxied {current.RequestsStarted} requests ({newRequests} in the last {(int)elapsed.TotalMilliseconds} ms)";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Code": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/ReverseProxy.Code.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(TestTFMs)
5 | Exe
6 | Yarp.ReverseProxy.Sample
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/TokenService.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Security.Claims;
5 | using System.Threading.Tasks;
6 |
7 | namespace Yarp.ReverseProxy.Sample;
8 |
9 | internal sealed class TokenService
10 | {
11 | internal Task GetAuthTokenAsync(ClaimsPrincipal user)
12 | {
13 | return Task.FromResult(user.Identity.Name);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Code/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Config/Controllers/HealthController.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace Yarp.ReverseProxy.Sample.Controllers;
7 |
8 | ///
9 | /// Controller for health check api.
10 | ///
11 | [ApiController]
12 | public class HealthController : ControllerBase
13 | {
14 | ///
15 | /// Returns 200 if Proxy is healthy.
16 | ///
17 | [HttpGet]
18 | [Route("/api/health")]
19 | public IActionResult CheckHealth()
20 | {
21 | return Ok();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Config/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Config": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Config/ReverseProxy.Config.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(TestTFMs)
5 | Exe
6 | Yarp.ReverseProxy.Sample
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Config/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Direct/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44356/",
7 | "sslPort": 44356
8 | }
9 | },
10 | "profiles": {
11 | "ReverseProxy.Direct": {
12 | "commandName": "Project",
13 | "launchBrowser": false,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "IIS Express": {
19 | "commandName": "IISExpress",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Direct/ReverseProxy.Direct.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(TestTFMs)
5 | Exe
6 | Yarp.ReverseProxy.Sample
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Direct/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "Microsoft": "Debug",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/testassets/ReverseProxy.Direct/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/testassets/TestClient/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "TestClient": {
4 | "commandName": "Project",
5 | "commandLineArgs": "-t https://localhost:5001"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/testassets/TestClient/Scenarios/IScenario.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace SampleClient.Scenarios;
8 |
9 | ///
10 | /// Interface for the implementation of a scenario that can be executed asynchronously.
11 | ///
12 | internal interface IScenario
13 | {
14 | ///
15 | /// Executes the scenario asynchronously.
16 | ///
17 | Task ExecuteAsync(CommandLineArgs args, CancellationToken cancellation);
18 | }
19 |
--------------------------------------------------------------------------------
/testassets/TestClient/TestClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(LatestDevTFM)
5 | Exe
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/testassets/TestServer/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
--------------------------------------------------------------------------------
/testassets/TestServer/Controllers/HealthController.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace Yarp.ReverseProxy.Sample.Controllers;
7 |
8 | ///
9 | /// Controller for active health check probes.
10 | ///
11 | [ApiController]
12 | public class HealthController : ControllerBase
13 | {
14 | private static volatile int _count;
15 | ///
16 | /// Returns 200 if server is healthy.
17 | ///
18 | [HttpGet]
19 | [Route("/api/health")]
20 | public IActionResult CheckHealth()
21 | {
22 | _count++;
23 | // Simulate temporary health degradation.
24 | return _count % 10 < 4 ? Ok() : StatusCode(500);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/testassets/TestServer/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 |
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | builder.Services.AddControllers()
10 | .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
11 |
12 | var app = builder.Build();
13 |
14 | app.UseWebSockets();
15 | app.MapControllers();
16 |
17 | app.Run();
18 |
--------------------------------------------------------------------------------
/testassets/TestServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "TestServer": {
4 | "commandName": "Project",
5 | "launchBrowser": false,
6 | "environmentVariables": {
7 | "ASPNETCORE_ENVIRONMENT": "Development"
8 | },
9 | "applicationUrl": "https://localhost:10000;https://localhost:10001;http://localhost:10010;http://localhost:10011"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/testassets/TestServer/TestServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(LatestDevTFM)
5 | Exe
6 | SampleServer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/testassets/TestServer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Information",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/testassets/TestServer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
--------------------------------------------------------------------------------