From d98e0238dc9b8595d15b810d6455b8eb84ea5fcf Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 6 Feb 2025 22:59:13 +0100 Subject: [PATCH 1/5] Remove and update docs --- .github/workflows/docfx_build.yml | 51 - YARP.sln | 13 - docs/README.md | 3 +- docs/designs/config.md | 3 + docs/designs/route-extensibility.md | 3 + docs/designs/yarp-tunneling.md | 3 + docs/docfx/.gitignore | 10 - docs/docfx/README.md | 19 - docs/docfx/api/.gitignore | 5 - docs/docfx/api/index.md | 5 - docs/docfx/articles/ab-testing.md | 45 - docs/docfx/articles/authn-authz.md | 101 -- docs/docfx/articles/config-files.md | 225 --- docs/docfx/articles/config-filters.md | 92 -- docs/docfx/articles/config-providers.md | 87 -- docs/docfx/articles/cors.md | 67 - docs/docfx/articles/destination-resolvers.md | 58 - docs/docfx/articles/dests-health-checks.md | 389 ------ docs/docfx/articles/diagnosing-yarp-issues.md | 239 ---- docs/docfx/articles/direct-forwarding.md | 173 --- docs/docfx/articles/distributed-tracing.md | 135 -- docs/docfx/articles/getting-started.md | 95 -- docs/docfx/articles/grpc.md | 53 - docs/docfx/articles/header-guidelines.md | 80 -- docs/docfx/articles/header-routing.md | 424 ------ docs/docfx/articles/http-client-config.md | 239 ---- docs/docfx/articles/http3.md | 27 - docs/docfx/articles/https-tls.md | 29 - docs/docfx/articles/httpsys-delegation.md | 71 - docs/docfx/articles/index.md | 10 - docs/docfx/articles/kubernetes-ingress.md | 109 -- docs/docfx/articles/lets-encrypt.md | 68 - docs/docfx/articles/load-balancing.md | 103 -- docs/docfx/articles/middleware.md | 148 -- docs/docfx/articles/output-caching.md | 63 - docs/docfx/articles/packages-refs.md | 128 -- docs/docfx/articles/queryparameter-routing.md | 363 ----- docs/docfx/articles/rate-limiting.md | 74 - docs/docfx/articles/runtimes.md | 27 - docs/docfx/articles/service-fabric-int.md | 3 - docs/docfx/articles/session-affinity.md | 118 -- docs/docfx/articles/timeouts.md | 77 -- docs/docfx/articles/toc.yml | 64 - docs/docfx/articles/transforms.md | 1203 ----------------- docs/docfx/articles/websockets.md | 27 - docs/docfx/docfx.csproj | 16 - docs/docfx/docfx.json | 71 - docs/docfx/index.md | 27 - docs/docfx/templates/logo_template/logo.svg | 9 - docs/docfx/toc.yml | 8 - docs/operations/BackportingToPreview.md | 7 +- docs/operations/DependencyFlow.md | 122 +- docs/operations/README.md | 3 +- docs/operations/Release.md | 13 +- eng/Build.props | 1 - eng/Versions.props | 1 - 56 files changed, 51 insertions(+), 5556 deletions(-) delete mode 100644 .github/workflows/docfx_build.yml delete mode 100644 docs/docfx/.gitignore delete mode 100644 docs/docfx/README.md delete mode 100644 docs/docfx/api/.gitignore delete mode 100644 docs/docfx/api/index.md delete mode 100644 docs/docfx/articles/ab-testing.md delete mode 100644 docs/docfx/articles/authn-authz.md delete mode 100644 docs/docfx/articles/config-files.md delete mode 100644 docs/docfx/articles/config-filters.md delete mode 100644 docs/docfx/articles/config-providers.md delete mode 100644 docs/docfx/articles/cors.md delete mode 100644 docs/docfx/articles/destination-resolvers.md delete mode 100644 docs/docfx/articles/dests-health-checks.md delete mode 100644 docs/docfx/articles/diagnosing-yarp-issues.md delete mode 100644 docs/docfx/articles/direct-forwarding.md delete mode 100644 docs/docfx/articles/distributed-tracing.md delete mode 100644 docs/docfx/articles/getting-started.md delete mode 100644 docs/docfx/articles/grpc.md delete mode 100644 docs/docfx/articles/header-guidelines.md delete mode 100644 docs/docfx/articles/header-routing.md delete mode 100644 docs/docfx/articles/http-client-config.md delete mode 100644 docs/docfx/articles/http3.md delete mode 100644 docs/docfx/articles/https-tls.md delete mode 100644 docs/docfx/articles/httpsys-delegation.md delete mode 100644 docs/docfx/articles/index.md delete mode 100644 docs/docfx/articles/kubernetes-ingress.md delete mode 100644 docs/docfx/articles/lets-encrypt.md delete mode 100644 docs/docfx/articles/load-balancing.md delete mode 100644 docs/docfx/articles/middleware.md delete mode 100644 docs/docfx/articles/output-caching.md delete mode 100644 docs/docfx/articles/packages-refs.md delete mode 100644 docs/docfx/articles/queryparameter-routing.md delete mode 100644 docs/docfx/articles/rate-limiting.md delete mode 100644 docs/docfx/articles/runtimes.md delete mode 100644 docs/docfx/articles/service-fabric-int.md delete mode 100644 docs/docfx/articles/session-affinity.md delete mode 100644 docs/docfx/articles/timeouts.md delete mode 100644 docs/docfx/articles/toc.yml delete mode 100644 docs/docfx/articles/transforms.md delete mode 100644 docs/docfx/articles/websockets.md delete mode 100644 docs/docfx/docfx.csproj delete mode 100644 docs/docfx/docfx.json delete mode 100644 docs/docfx/index.md delete mode 100644 docs/docfx/templates/logo_template/logo.svg delete mode 100644 docs/docfx/toc.yml diff --git a/.github/workflows/docfx_build.yml b/.github/workflows/docfx_build.yml deleted file mode 100644 index fdadc42d6..000000000 --- a/.github/workflows/docfx_build.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: DocFX Build -on: - push: - branches: - - release/latest -jobs: - build: - name: Build - runs-on: windows-latest - permissions: - contents: write # for push access to the gh-pages branch - steps: - # Check out the branch that triggered this workflow to the 'source' subdirectory - - name: Checkout Code - uses: actions/checkout@v2 - with: - path: source - ref: release/latest - # Run a build - - name: Build docs - run: "& ./eng/common/build.ps1 -restore -build -projects ./docs/docfx/docfx.csproj" - working-directory: ./source - # Check out gh-pages branch to the 'docs' subdirectory - - name: Checkout docs - uses: actions/checkout@v2 - with: - ref: gh-pages - path: docs - # Sync the site - - name: Clear docs repo - run: Get-ChildItem -Force -Exclude .git | ForEach-Object { Remove-Item -Recurse -Verbose -Force $_ } - working-directory: ./docs - - name: Sync new content - run: Copy-Item -Recurse -Verbose -Force "$env:GITHUB_WORKSPACE/source/docs/docfx/_site/*" . - working-directory: ./docs - # Commit - - name: Commit to gh-pages and push - run: | - $ErrorActionPreference = "Continue" - git add -A - git diff HEAD --exit-code - if ($LASTEXITCODE -eq 0) { - Write-Host "No changes to commit!" - } else { - git config --global user.name "github-actions[bot]" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - git commit -m"Updated docs from commit $env:GITHUB_SHA on $env:GITHUB_REF" - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} - git push origin gh-pages - } - working-directory: ./docs diff --git a/YARP.sln b/YARP.sln index 0aa7451aa..88cb98591 100644 --- a/YARP.sln +++ b/YARP.sln @@ -23,10 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0631147E-3 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6CBE18D4-64E9-492B-BB02-58CD57126C10}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{BC344A50-8F81-4762-9F4B-12714693144B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docfx", "docs\docfx\docfx.csproj", "{7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReverseProxy.Auth.Sample", "samples\ReverseProxy.Auth.Sample\ReverseProxy.Auth.Sample.csproj", "{354F2755-A090-4735-A657-726FB6DA92CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yarp.ReverseProxy.FunctionalTests", "test\ReverseProxy.FunctionalTests\Yarp.ReverseProxy.FunctionalTests.csproj", "{31089146-71DA-45C2-ACA6-EA1E2C916FD0}" @@ -134,14 +130,6 @@ Global {11D098B2-7116-4F37-817A-E496B8F15C76}.Release|Any CPU.Build.0 = Release|Any CPU {11D098B2-7116-4F37-817A-E496B8F15C76}.Release|x64.ActiveCfg = Release|Any CPU {11D098B2-7116-4F37-817A-E496B8F15C76}.Release|x64.Build.0 = Release|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Debug|x64.ActiveCfg = Debug|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Debug|x64.Build.0 = Debug|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Release|Any CPU.Build.0 = Release|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Release|x64.ActiveCfg = Release|Any CPU - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3}.Release|x64.Build.0 = Release|Any CPU {354F2755-A090-4735-A657-726FB6DA92CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {354F2755-A090-4735-A657-726FB6DA92CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {354F2755-A090-4735-A657-726FB6DA92CD}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -382,7 +370,6 @@ Global {568EF8AE-7624-490D-A19F-C25D076FF091} = {6CBE18D4-64E9-492B-BB02-58CD57126C10} {3B188E4C-C926-4BED-94F3-0E83668FBAB0} = {0631147E-34BB-456D-B214-5B202C516D5C} {11D098B2-7116-4F37-817A-E496B8F15C76} = {149C61A2-D9F8-49B9-9F9B-3C953FEF53AA} - {7F6D4710-07D1-49D0-8EAE-675A3A9B50E3} = {BC344A50-8F81-4762-9F4B-12714693144B} {354F2755-A090-4735-A657-726FB6DA92CD} = {149C61A2-D9F8-49B9-9F9B-3C953FEF53AA} {31089146-71DA-45C2-ACA6-EA1E2C916FD0} = {0631147E-34BB-456D-B214-5B202C516D5C} {F2547357-FB2F-4944-842F-D33D1E7A17FC} = {149C61A2-D9F8-49B9-9F9B-3C953FEF53AA} diff --git a/docs/README.md b/docs/README.md index 1564340f8..2d42bff55 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,4 +4,5 @@ This folder contains: * [Design Notes](designs/) - These are the design notes used to guide our development. They aren't designed to be usage guides but may help contributors in understanding why some patterns were used. * [Operations](operations/) - These are operational docs for running releases and other infrastructure related to the project. -* [Public Usage Docs](docfx/) - This is a [docfx](https://dotnet.github.com/docfx) project that generates our public docs site [microsoft.github.io/reverse-proxy](https://microsoft.github.io/reverse-proxy). Any commit to `main` will trigger the [GitHub Action](../.github/workflows/docfx_build.yml) to rebuild the docs site and push it to the `gh-pages` branch. + +Public documentation is available at [learn.microsoft.com](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/getting-started?view=aspnetcore-9.0). \ No newline at end of file diff --git a/docs/designs/config.md b/docs/designs/config.md index 30dbdc010..2fbe536ae 100644 --- a/docs/designs/config.md +++ b/docs/designs/config.md @@ -1,5 +1,8 @@ # Config based proxy apps +> [!CAUTION] +> These are archived design discussions. Information may be outdated and inaccurate. + RE: https://github.com/dotnet/yarp/issues/9 Config based proxies are common and we'll need to support at least basic proxy scenarios from config. Here are some initial considerations: diff --git a/docs/designs/route-extensibility.md b/docs/designs/route-extensibility.md index 73105b772..b9691da63 100644 --- a/docs/designs/route-extensibility.md +++ b/docs/designs/route-extensibility.md @@ -1,5 +1,8 @@ ## Problem Statement +> [!CAUTION] +> These are archived design discussions. Information may be outdated and inaccurate. + Today if you want to extend the route or clusters, you can only do it through the metadata property on each, which is a Dictionary. If you want to be able to have structured data its not possible without you forcing it into a string and then parsing it when needed. There are scenarios like A/B testing, or authenticating with back end servers (not pass thru) where you want to be able to store a structure of data in config, and have it available at runtime on the route/cluster objects. If we want there to be pre-built extensions to YARP (#1714), then there needs to be a way for each of the extensions to have its own config data on routes and clusters, and for them to not step on each others toes. diff --git a/docs/designs/yarp-tunneling.md b/docs/designs/yarp-tunneling.md index 163b01a3b..41d92a7cc 100644 --- a/docs/designs/yarp-tunneling.md +++ b/docs/designs/yarp-tunneling.md @@ -1,5 +1,8 @@ # YARP Tunneling +> [!CAUTION] +> These are archived design discussions. Information may be outdated and inaccurate. + ## Introduction While many organizations are moving their computing to the cloud, there are occasions where you need to be able to run some services in a local datacenter. The problem then is if you need to be able to communicate with those services from the cloud. Creating a VPN network connection to Azure or other cloud provider is possible, but usually involves a lot of red tape and configuration complexity as the two networks need to be integrated. diff --git a/docs/docfx/.gitignore b/docs/docfx/.gitignore deleted file mode 100644 index 6858b9860..000000000 --- a/docs/docfx/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -############### -# folder # -############### -/**/DROP/ -/**/TEMP/ -/**/packages/ -/**/bin/ -/**/obj/ -_site -log.txt diff --git a/docs/docfx/README.md b/docs/docfx/README.md deleted file mode 100644 index 9d3a96b02..000000000 --- a/docs/docfx/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# DocFx - -This project builds the API and concept docs for this reverse-proxy repo. It uses a library called [DocFx](https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html). - -## Bulding the docs - -The docs will be built with everything else when running `build.cmd/sh` in the repo root. - -## Testing the docs - -The build will produce a series of HTML files in the `_site` directory. Many of the links won't work if you try to open the HTML files directly. A tool like [dotnet-serve](https://github.com/natemcmaster/dotnet-serve) can be run in the `_site` directory to properly render the content. - -## Publishing the docs - -The docs are automatically built and published by a [GitHub Action](https://github.com/microsoft/reverse-proxy/blob/main/.github/workflows/docfx_build.yml) on every push to `release/latest`. The built `_site` directory is pushed to the `gh-pages` branch and served by [https://microsoft.github.io/reverse-proxy/](https://microsoft.github.io/reverse-proxy/). Maintaining a separate branch for the released docs allows us to choose when to publish them and with what content, and without modifying the build scripts each release. - -Doc edits for the current public release should go into that release's branch (e.g. `release/1.0.0-preview3`) and merged forward into `main` and `release/latest`. - -When publishing a new product version (e.g. `release/1.0.0-preview4`) `release/latest` should be merged to that position after the docs have been updated. diff --git a/docs/docfx/api/.gitignore b/docs/docfx/api/.gitignore deleted file mode 100644 index e8079a3be..000000000 --- a/docs/docfx/api/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -############### -# temp file # -############### -*.yml -.manifest diff --git a/docs/docfx/api/index.md b/docs/docfx/api/index.md deleted file mode 100644 index 82475095a..000000000 --- a/docs/docfx/api/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# API Documentation for YARP - -This is the API reference documentation for YARP 2.0. - -For documentation of YARP 1.1.1, see https://github.com/microsoft/reverse-proxy/tree/release/1.1/docs/docfx/articles. \ No newline at end of file diff --git a/docs/docfx/articles/ab-testing.md b/docs/docfx/articles/ab-testing.md deleted file mode 100644 index 10e496057..000000000 --- a/docs/docfx/articles/ab-testing.md +++ /dev/null @@ -1,45 +0,0 @@ -# A/B Testing and Rolling Upgrades - -## Introduction - -A/B testing and rolling upgrades require procedures for dynamically assigning incoming traffic to evaluate changes in the destination application. YARP does not have a built-in model for this, but it does expose some infrastructure useful for building such a system. See [issue #126](https://github.com/microsoft/reverse-proxy/issues/126) for additional details about this scenario. - -## Example - -``` -app.MapReverseProxy(proxyPipeline => -{ - // Custom cluster selection - proxyPipeline.Use((context, next) => - { - var lookup = context.RequestServices.GetRequiredService(); - if (lookup.TryGetCluster(ChooseCluster(context), out var cluster)) - { - context.ReassignProxyRequest(cluster); - } - - return next(); - }); - proxyPipeline.UseSessionAffinity(); - proxyPipeline.UseLoadBalancing(); -}); - -string ChooseCluster(HttpContext context) -{ - // Decide which cluster to use. This could be random, weighted, based on headers, etc. - return Random.Shared.Next(2) == 1 ? "cluster1" : "cluster2"; -} -``` - -## Usage - -This scenario makes use of two APIs, [IProxyStateLookup](xref:Yarp.ReverseProxy.IProxyStateLookup) and [ReassignProxyRequest](xref:Microsoft.AspNetCore.Http.HttpContextFeaturesExtensions.ReassignProxyRequest(Microsoft.AspNetCore.Http.HttpContext,Yarp.ReverseProxy.Model.ClusterState)), called from a custom proxy middleware as shown in the sample above. - -`IProxyStateLookup` is a service available in the Dependency Injection container that can be used to look up or enumerate the current routes and clusters. Note this data may change if the configuration changes. An A/B orchestration algorithm can examine the request, decide which cluster to send it to, and then retrieve that cluster from `IProxyStateLookup.TryGetCluster`. - -Once the cluster is selected, `ReassignProxyRequest` can be called to assign the request to that cluster. This updates the [IReverseProxyFeature](xref:Yarp.ReverseProxy.Model.IReverseProxyFeature) with the new cluster and destination information needed for the rest of the proxy middleware pipeline to handle the request. - -## Session affinity - -Note that session affinity functionality is split between middleware, which reads it settings from the current cluster, and transforms, which are part of the original route. Clusters used for A/B testing should use the same session affinity configuration to avoid conflicts. - diff --git a/docs/docfx/articles/authn-authz.md b/docs/docfx/articles/authn-authz.md deleted file mode 100644 index e9a44018c..000000000 --- a/docs/docfx/articles/authn-authz.md +++ /dev/null @@ -1,101 +0,0 @@ -# Authentication and Authorization - -## Introduction -The reverse proxy can be used to authenticate and authorize requests before they are proxied to the destination servers. This can reduce load on the destination servers, add a layer of protection, and ensure consistent policies are implemented across your applications. - -## Defaults - -No authentication or authorization is performed on requests unless enabled in the route or application configuration. - -## Configuration -Authorization policies can be specified per route via [RouteConfig.AuthorizationPolicy](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "AuthorizationPolicy": "customPolicy", - "Match": { - "Hosts": [ "localhost" ] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } - } -} -``` - -[Authorization policies](https://docs.microsoft.com/aspnet/core/security/authorization/policies) are an ASP.NET Core concept that the proxy utilizes. The proxy provides the above configuration to specify a policy per route and the rest is handled by existing ASP.NET Core authentication and authorization components. - -Authorization policies can be configured in the application as follows: -``` -services.AddAuthorization(options => -{ - options.AddPolicy("customPolicy", policy => - policy.RequireAuthenticatedUser()); -}); -``` - -In Program.cs add the Authorization and Authentication middleware. - -``` -app.UseAuthentication(); -app.UseAuthorization(); - -app.MapReverseProxy(); -``` - -See the [Authentication](https://docs.microsoft.com/aspnet/core/security/authentication/) docs for setting up your preferred kind of authentication. - -### Special values: - -In addition to custom policy names, there are two special values that can be specified in a route's authorization parameter: `default` and `anonymous`. ASP.NET Core also has a FallbackPolicy setting that applies to routes that do not specify a policy. - -#### DefaultPolicy - -Specifying the value `default` in a route's authorization parameter means that route will use the policy defined in [AuthorizationOptions.DefaultPolicy](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.authorization.authorizationoptions.defaultpolicy?#Microsoft_AspNetCore_Authorization_AuthorizationOptions_DefaultPolicy). That policy is pre-configured to require authenticated users. - -#### Anonymous - -Specifying the value `anonymous` in a route's authorization parameter means that route will not require authorization regardless of any other configuration in the application such as the FallbackPolicy. - -#### FallbackPolicy - -[AuthorizationOptions.FallbackPolicy](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.authorization.authorizationoptions.fallbackpolicy) is the policy that will be used for any request or route that was not configured with a policy. FallbackPolicy does not have a value by default, any request will be allowed. - -## Flowing Credentials - -Even after a request has been authorized in the proxy, the destination server may still need to know who the user is (authentication) and what they're allowed to do (authorization). How you flow that information will depend on the type of authentication being used. - -### Cookie, bearer, API keys - -These authentication types already pass their values in the request headers and these will flow to the destination server by default. That server will still need to verify and interpret those values, causing some double work. - -### OAuth2, OpenIdConnect, WsFederation - -These protocols are commonly used with remote identity providers. The authentication process can be configured in the proxy application and will result in an authentication cookie. That cookie will flow to the destination server as a normal request header. - -### Windows, Negotiate, NTLM, Kerberos - -These authentication types are often bound to a specific connection. They are not supported as means of authenticating a user in a destination server behind the YARP proxy (see [#166](https://github.com/microsoft/reverse-proxy/issues/166). They can be used to authenticate an incoming request to the proxy, but that identity information will have to be communicated to the destination server in another form. They can also be used to authenticate the proxy to the destination servers, but only as the proxy's own user, impersonating the client is not supported. - -### Client Certificates - -Client certificates are a TLS feature and are negotiated as part of a connection. See [these docs](https://docs.microsoft.com/aspnet/core/security/authentication/certauth) for additional information. The certificate can be forwarded to the destination server as an HTTP header using the [ClientCert](transforms.md#clientcert) transform. - -### Swapping authentication types - -Authentication types like Windows that don't flow naturally to the destination server will need to be converted in the proxy to an alternate form. For example a JWT bearer token can be created with the user information and set on the proxy request. - -These swaps can be performed using [custom request transforms](transforms.md#from-code). Detailed examples can be developed for specific scenarios if there is enough community interest. We need more community feedback on how you want to convert and flow identity information. diff --git a/docs/docfx/articles/config-files.md b/docs/docfx/articles/config-files.md deleted file mode 100644 index ca6cb4a57..000000000 --- a/docs/docfx/articles/config-files.md +++ /dev/null @@ -1,225 +0,0 @@ -# Configuration Files - -## Introduction -The reverse proxy can load configuration for routes and clusters from files using the IConfiguration abstraction from Microsoft.Extensions. The examples given here use JSON, but any IConfiguration source should work. The configuration will also be updated without restarting the proxy if the source file changes. - -## Loading Configuration -To load the proxy configuration from IConfiguration add the following code in Program.cs: -```c# -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); - -// Add the reverse proxy capability to the server -builder.Services.AddReverseProxy() - // Initialize the reverse proxy from the "ReverseProxy" section of configuration - .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); - -var app = builder.Build(); - -// Register the reverse proxy routes -app.MapReverseProxy(); - -app.Run(); -``` -**Note**: For details about middleware ordering see [here](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/#middleware-order). - -Configuration can be modified during the load sequence using [Configuration Filters](config-filters.md). - -## Multiple Configuration Sources -As of 1.1, YARP supports loading the proxy configuration from multiple sources. LoadFromConfig may be called multiple times referencing different IConfiguration sections or may be combine with a different config source like InMemory. Routes can reference clusters from other sources. Note merging partial config from different sources for a given route or cluster is not supported. - -```c# -services.AddReverseProxy() - .LoadFromConfig(Configuration.GetSection("ReverseProxy1")) - .LoadFromConfig(Configuration.GetSection("ReverseProxy2")); -``` -or -```c# - -services.AddReverseProxy() - .LoadFromMemory(routes, clusters) - .LoadFromConfig(Configuration.GetSection("ReverseProxy")); -``` - -## Configuration contract -File-based configuration is dynamically mapped to the types in [Yarp.ReverseProxy.Configuration](xref:Yarp.ReverseProxy.Configuration) namespace by an [IProxyConfigProvider](xref:Yarp.ReverseProxy.Configuration.IProxyConfigProvider) implementation converts at application start and each time the configuration changes. - -## Configuration Structure -The configuration consists of a named section that you specified above via `Configuration.GetSection("ReverseProxy")`, and contains subsections for routes and clusters. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://example.com/" - } - } - } - } - } -} -``` - -### Routes - -The routes section is an unordered collection of route matches and their associated configuration. A route requires at least the following fields: -- RouteId - a unique name -- ClusterId - refers to the name of an entry in the clusters section. -- Match - contains either a Hosts array or a Path pattern string. Path is an ASP.NET Core route template that can be defined as [explained here](https://docs.microsoft.com/aspnet/core/fundamentals/routing#route-templates). -Route matching is based on the most specific routes having highest precedence as described [here]( https://docs.microsoft.com/aspnet/core/fundamentals/routing#url-matching). Explicit ordering can be achieved using the `order` field, with lower values taking higher priority. - -[Headers](header-routing.md), [Authorization](authn-authz.md), [CORS](cors.md), and other route based policies can be configured on each route entry. For additional fields see [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig). - -The proxy will apply the given matching criteria and policies, and then pass off the request to the specified cluster. - -### Clusters -The clusters section is an unordered collection of named clusters. A cluster primarily contains a collection of named destinations and their addresses, any of which is considered capable of handling requests for a given route. The proxy will process the request according to the route and cluster configuration in order to select a destination. - -For additional fields see [ClusterConfig](xref:Yarp.ReverseProxy.Configuration.ClusterConfig). - -## All config properties -```JSON -{ - // Base URLs the server listens on, must be configured independently of the routes below - "Urls": "http://localhost:5000;https://localhost:5001", - "Logging": { - "LogLevel": { - "Default": "Information", - // Uncomment to hide diagnostic messages from runtime and proxy - // "Microsoft": "Warning", - // "Yarp" : "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "ReverseProxy": { - // Routes tell the proxy which requests to forward - "Routes": { - "minimumroute" : { - // Matches anything and routes it to www.example.com - "ClusterId": "minimumcluster", - "Match": { - "Path": "{**catch-all}" - } - }, - "allrouteprops" : { - // matches /something/* and routes to "allclusterprops" - "ClusterId": "allclusterprops", // Name of one of the clusters - "Order" : 100, // Lower numbers have higher precedence - "MaxRequestBodySize" : 1000000, // In bytes. An optional override of the server's limit (30MB default). Set to -1 to disable. - "AuthorizationPolicy" : "Anonymous", // Name of the policy or "Default", "Anonymous" - "CorsPolicy" : "Default", // Name of the CorsPolicy to apply to this route or "Default", "Disable" - "Match": { - "Path": "/something/{**remainder}", // The path to match using ASP.NET syntax. - "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], // The host names to match, unspecified is any - "Methods" : [ "GET", "PUT" ], // The HTTP methods that match, uspecified is all - "Headers": [ // The headers to match, unspecified is any - { - "Name": "MyCustomHeader", // Name of the header - "Values": [ "value1", "value2", "another value" ], // Matches are against any of these values - "Mode": "ExactHeader", // or "HeaderPrefix", "Exists" , "Contains", "NotContains", "NotExists" - "IsCaseSensitive": true - } - ], - "QueryParameters": [ // The query parameters to match, unspecified is any - { - "Name": "MyQueryParameter", // Name of the query parameter - "Values": [ "value1", "value2", "another value" ], // Matches are against any of these values - "Mode": "Exact", // or "Prefix", "Exists" , "Contains", "NotContains" - "IsCaseSensitive": true - } - ] - }, - "Metadata" : { // List of key value pairs that can be used by custom extensions - "MyName" : "MyValue" - }, - "Transforms" : [ // List of transforms. See the Transforms article for more details - { - "RequestHeader": "MyHeader", - "Set": "MyValue" - } - ] - } - }, - // Clusters tell the proxy where and how to forward requests - "Clusters": { - "minimumcluster": { - "Destinations": { - "example.com": { - "Address": "http://www.example.com/" - } - } - }, - "allclusterprops": { - "Destinations": { - "first_destination": { - "Address": "https://contoso.com" - }, - "another_destination": { - "Address": "https://10.20.30.40", - "Health" : "https://10.20.30.40:12345/test" // override for active health checks - } - }, - "LoadBalancingPolicy" : "PowerOfTwoChoices", // Alternatively "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests" - "SessionAffinity": { - "Enabled": true, // Defaults to 'false' - "Policy": "Cookie", // Default, alternatively "CustomHeader" - "FailurePolicy": "Redistribute", // default, Alternatively "Return503Error" - "Settings" : { - "CustomHeaderName": "MySessionHeaderName" // Defaults to 'X-Yarp-Proxy-Affinity` - } - }, - "HealthCheck": { - "Active": { // Makes API calls to validate the health. - "Enabled": "true", - "Interval": "00:00:10", - "Timeout": "00:00:10", - "Policy": "ConsecutiveFailures", - "Path": "/api/health", // API endpoint to query for health state - "Query": "?foo=bar" - }, - "Passive": { // Disables destinations based on HTTP response codes - "Enabled": true, // Defaults to false - "Policy" : "TransportFailureRateHealthPolicy", // Required - "ReactivationPeriod" : "00:00:10" // 10s - } - }, - "HttpClient" : { // Configuration of HttpClient instance used to contact destinations - "SSLProtocols" : "Tls13", - "DangerousAcceptAnyServerCertificate" : false, - "MaxConnectionsPerServer" : 1024, - "EnableMultipleHttp2Connections" : true, - "RequestHeaderEncoding" : "Latin1", // How to interpret non ASCII characters in request header values - "ResponseHeaderEncoding" : "Latin1" // How to interpret non ASCII characters in response header values - }, - "HttpRequest" : { // Options for sending request to destination - "ActivityTimeout" : "00:02:00", - "Version" : "2", - "VersionPolicy" : "RequestVersionOrLower", - "AllowResponseBuffering" : "false" - }, - "Metadata" : { // Custom Key value pairs - "TransportFailureRateHealthPolicy.RateLimit": "0.5", // Used by Passive health policy - "MyKey" : "MyValue" - } - } - } - } -} -``` - -For more information see [logging configuration](diagnosing-yarp-issues.md#logging) and [HTTP client configuration](http-client-config.md). diff --git a/docs/docfx/articles/config-filters.md b/docs/docfx/articles/config-filters.md deleted file mode 100644 index 83acc259e..000000000 --- a/docs/docfx/articles/config-filters.md +++ /dev/null @@ -1,92 +0,0 @@ -# Configuration Filters - -## Introduction -YARP configuration for routes, clusters, and destinations can be loaded from [configuration files](config-files.md) or from [configuration providers](config-providers.md). Configuration filters can be used to modify that raw input before it's validated and applied. - -Filters can be used for a variety of purposes such as: -- Supplementing config fields with data from other sources like the deployment environment -- Applying system defaults -- Applying common settings and enforce policies -- Substituting placeholder values -- Normalization and error correction - -## AddConfigFilter -Configuration filters are registered in the Dependency Injection system using the [AddConfigFilter](xref:Microsoft.Extensions.DependencyInjection.ReverseProxyServiceCollectionExtensions) API. Any number of unique filters can be added and will be applied in the order added. - -```C# -// Load the configuration and register a config filter -services.AddReverseProxy() - .LoadFromConfig(_configuration.GetSection("ReverseProxy")) - .AddConfigFilter(); -``` - -## IProxyConfigFilter -Configuration filters are written using the [IProxyConfigFilter](xref:Yarp.ReverseProxy.Configuration.IProxyConfigFilter) interface. Because filters are registered in DI they may define a constructor to inject any dependencies. - -Filters are called for each route and cluster each time configuration is loaded or re-loaded. They can choose to return the original input unmodified or a modified copy. The C# 9 [records syntax](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/record#nondestructive-mutation) is convenient for making modified copies. - -This example fills in destination addresses from environment variables and sets the route's Order field to 1. - -```C# -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using Yarp.ReverseProxy.Configuration; - -namespace Yarp.Sample; - -public class CustomConfigFilter : IProxyConfigFilter -{ - // Matches {{env_var_name}} - private readonly Regex _exp = new("\\{\\{(\\w+)\\}\\}"); - - // This sample looks at the destination addresses and any of the form {{key}} will be modified, looking up the key - // as an environment variable. This is useful when hosted in Azure etc, as it enables a simple way to replace - // destination addresses via the management console - public ValueTask ConfigureClusterAsync(ClusterConfig origCluster, CancellationToken cancel) - { - // Each cluster has a dictionary of destinations, which is read-only, so we'll create a new one with our updates - var newDests = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var d in origCluster.Destinations) - { - var origAddress = d.Value.Address; - if (_exp.IsMatch(origAddress)) - { - // Get the name of the env variable from the destination and lookup value - var lookup = _exp.Matches(origAddress)[0].Groups[1].Value; - var newAddress = Environment.GetEnvironmentVariable(lookup); - - if (string.IsNullOrWhiteSpace(newAddress)) - { - throw new ArgumentException($"Configuration Filter Error: Substitution for '{lookup}' in cluster '{d.Key}' not found as an environment variable."); - } - - // using c# 9 "with" to clone and initialize a new record - var modifiedDest = d.Value with { Address = newAddress }; - newDests.Add(d.Key, modifiedDest); - } - else - { - newDests.Add(d.Key, d.Value); - } - } - - return new ValueTask(origCluster with { Destinations = newDests }); - } - - public ValueTask ConfigureRouteAsync(RouteConfig route, ClusterConfig cluster, CancellationToken cancel) - { - // Example: do not let config based routes take priority over code based routes. - // Lower numbers are higher priority. Code routes default to 0. - if (route.Order.HasValue && route.Order.Value < 1) - { - return new ValueTask(route with { Order = 1 }); - } - - return new ValueTask(route); - } -} -``` diff --git a/docs/docfx/articles/config-providers.md b/docs/docfx/articles/config-providers.md deleted file mode 100644 index 4516c6fbe..000000000 --- a/docs/docfx/articles/config-providers.md +++ /dev/null @@ -1,87 +0,0 @@ -# Extensibility: Configuration Providers - -## Introduction -The [Basic Yarp Sample](https://github.com/microsoft/reverse-proxy/tree/main/samples/BasicYarpSample) show proxy configuration being loaded from appsettings.json. Instead proxy configuration can be loaded programmatically from the source of your choosing. You do this by providing a couple of classes implementing [IProxyConfigProvider](xref:Yarp.ReverseProxy.Configuration.IProxyConfigProvider) and [IProxyConfig](xref:Yarp.ReverseProxy.Configuration.IProxyConfig). - -See [ReverseProxy.Code.Sample](https://github.com/microsoft/reverse-proxy/tree/main/samples/ReverseProxy.Code.Sample) for an example of a custom configuration provider. - -Configuration can be modified during the load sequence using [Configuration Filters](config-filters.md). - -## Structure -[IProxyConfigProvider](xref:Yarp.ReverseProxy.Configuration.IProxyConfigProvider) has a single method `GetConfig()` that should return an [IProxyConfig](xref:Yarp.ReverseProxy.Configuration.IProxyConfig) instance. The IProxyConfig has lists of the current routes and clusters, as well as an `IChangeToken` to notify the proxy when this information is out of date and should be reloaded, which will cause `GetConfig()` to be called again. - -### Routes -The routes section is an unordered collection of named routes. A route contains matches and their associated configuration. A route requires at least the following fields: -- RouteId - a unique name -- ClusterId - refers to the name of an entry in the clusters section. -- Match - contains either a Hosts array or a Path pattern string. Path is an ASP.NET Core route template that can be defined as [explained here](https://docs.microsoft.com/aspnet/core/fundamentals/routing#route-templates). - -[Headers](header-routing.md), [Authorization](authn-authz.md), [CORS](cors.md), and other route based policies can be configured on each route entry. For additional fields see [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig). - -The proxy will apply the given matching criteria and policies, and then pass off the request to the specified cluster. - -### Clusters -The clusters section is an unordered collection of named clusters. A cluster primarily contains a collection of named destinations and their addresses, any of which is considered capable of handling requests for a given route. The proxy will process the request according to the route and cluster configuration to select a destination. - -For additional fields see [ClusterConfig](xref:Yarp.ReverseProxy.Configuration.ClusterConfig). - -## In Memory Config - -The `InMemoryConfigProvider` implements `IProxyConfigProvider` and enables specifying routes and clusters directly in code by calling `LoadFromMemory`. - -``` -services.AddReverseProxy().LoadFromMemory(routes, clusters); -``` - -To update the config later, resolve the `InMemoryConfigProvider` from the services container and call `Update` with the new lists of routes and clusters. - -``` -httpContext.RequestServices.GetRequiredService().Update(routes, clusters); -``` - -## Lifecycle - -### Startup -The `IProxyConfigProvider` should be registered in the DI container as a singleton. At startup, the proxy will resolve this instance and call `GetConfig()`. On this first call the provider may choose to: -- Throw an exception if the provider cannot produce a valid proxy configuration for any reason. This will prevent the application from starting. -- Synchronously block while it loads the configuration. This will block the application from starting until valid route data is available. -- Or, it may choose to return an empty `IProxyConfig` instance while it loads the configuration in the background. The provider will need to trigger the `IChangeToken` when the configuration is available. - -The proxy will validate the given configuration and if it's invalid, an exception will be thrown that prevents the application from starting. The provider can avoid this by using the [IConfigValidator](xref:Yarp.ReverseProxy.Configuration.IConfigValidator) to pre-validate routes and clusters and take whatever action it deems appropriate such as excluding invalid entries. - -### Atomicity -The configuration objects and collections supplied to the proxy should be read-only and not modified once they have been handed to the proxy via `GetConfig()`. - -### Reload -If the `IChangeToken` supports `ActiveChangeCallbacks`, once the proxy has processed the initial set of configurations it will register a callback with this token. If the provider does not support callbacks then `HasChanged` will be polled every 5 minutes. - -When the provider wants to provide a new configuration to the proxy it should: -- load that configuration in the background. - - Route and cluster objects are immutable, so new instances have to be created for any new data. - - Objects for unchanged routes and clusters can be re-used, or new instances can be created - changes will be detected by diffing them. -- optionally validate the configuration using the [IConfigValidator](xref:Yarp.ReverseProxy.Configuration.IConfigValidator), and only then signal the `IChangeToken` from the prior `IProxyConfig` instance that new data is available. The proxy will call `GetConfig()` again to retrieve the new data. - -There are important differences when reloading configuration vs the first configuration load. -- The new configuration will be diffed against the current one and only modified routes or clusters will be updated. The update will be applied atomically and will only affect new requests, not requests currently in progress. -- Any errors in the reload process will be logged and suppressed. The application will continue using the last known good configuration. -- If `GetConfig()` throws the proxy will be unable to listen for future changes because `IChangeToken`s are single-use. - -Once the new configuration has been validated and applied, the proxy will register a callback with the new `IChangeToken`. Note if there are multiple reloads signaled in close succession, the proxy may skip some and load the next available configuration as soon as it's ready. Each `IProxyConfig` contains the full configuration state so nothing will be lost. - -## Multiple Configuration Sources -As of 1.1, YARP supports loading the proxy configuration from multiple sources. Multiple `IProxyConfigProvider`'s can be registered as singleton services and all will be resolved and combined. The sources may be the same or different types such as IConfiguration or InMemory. Routes can reference clusters from other sources. Note merging partial config from different sources for a given route or cluster is not supported. - -``` - services.AddReverseProxy() - .LoadFromConfig(Configuration.GetSection("ReverseProxy1")) - .LoadFromConfig(Configuration.GetSection("ReverseProxy2")); -``` -or -``` - services.AddReverseProxy() - .LoadFromMemory(routes, clusters) - .LoadFromConfig(Configuration.GetSection("ReverseProxy")); -``` - -## Example -The [InMemoryConfigProvider](https://github.com/microsoft/reverse-proxy/blob/main/src/ReverseProxy/Configuration/InMemoryConfigProvider.cs) gives an example of an `IProxyConfigProvider` that has routes and clusters manually loaded into it. diff --git a/docs/docfx/articles/cors.md b/docs/docfx/articles/cors.md deleted file mode 100644 index dc60b0584..000000000 --- a/docs/docfx/articles/cors.md +++ /dev/null @@ -1,67 +0,0 @@ -# Cross-Origin Requests (CORS) - -## Introduction - -The reverse proxy can handle cross-origin requests before they are proxied to the destination servers. This can reduce load on the destination servers and ensure consistent policies are implemented across your applications. - -## Defaults -The requests won't be automatically matched for cors preflight requests unless enabled in the route or application configuration. - -## Configuration -CORS policies can be specified per route via [RouteConfig.CorsPolicy](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "CorsPolicy": "customPolicy", - "Match": { - "Hosts": [ "localhost" ] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } - } -} -``` - -[CORS policies](https://docs.microsoft.com/aspnet/core/security/cors#cors-with-named-policy-and-middleware) are an ASP.NET Core concept that the proxy utilizes. The proxy provides the above configuration to specify a policy per route and the rest is handled by existing ASP.NET Core CORS Middleware. - -CORS policies can be configured in the application as follows: -``` -services.AddCors(options => -{ - options.AddPolicy("customPolicy", builder => - { - builder.AllowAnyOrigin(); - }); -}); -``` - -Then add the CORS middleware. - -``` -app.UseCors(); - -app.MapReverseProxy(); -``` - - -### DefaultPolicy - -Specifying the value `default` in a route's `CorsPolicy` parameter means that route will use the policy defined in [CorsOptions.DefaultPolicy](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.cors.infrastructure.corsoptions.defaultpolicyname). - -### Disable CORS - -Specifying the value `disable` in a route's `CorsPolicy` parameter means the CORS middleware will refuse the CORS requests. diff --git a/docs/docfx/articles/destination-resolvers.md b/docs/docfx/articles/destination-resolvers.md deleted file mode 100644 index e3ebd89ca..000000000 --- a/docs/docfx/articles/destination-resolvers.md +++ /dev/null @@ -1,58 +0,0 @@ -# Extensibility: Destination Resolvers - -## Introduction - -YARP uses a destination resolver to expand the set of configured destination addresses. The destination resolver can be used as an integration point with service discovery systems. - -## Structure -[IDestinationResolver](xref:Yarp.ReverseProxy.ServiceDiscovery.IDestinationResolver) has a single method `ResolveDestinationsAsync(IReadOnlyDictionary destinations, CancellationToken cancellationToken)` which should return a [ResolvedDestinationCollection](xref:Yarp.ReverseProxy.ServiceDiscovery.ResolvedDestinationCollection) instance. The [ResolvedDestinationCollection](xref:Yarp.ReverseProxy.ServiceDiscovery.ResolvedDestinationCollection) has a collection of [DestinationConfig](xref:Yarp.ReverseProxy.Configuration.DestinationConfig) instances, as well as an `IChangeToken` to notify the proxy when this information is out of date and should be reloaded, which will cause `ResolveDestinationsAsync` to be called again. - -### DestinationConfig -`DestinationConfig` has a `Host` property which can be used to specify the default `Host` header value which the proxy should use when communicating with that destination. This allows the `IDestinationResolver` to resolve destinations to a collection of IP addresses, for example, without causing SNI or host-based routing to fail. - -## Lifecycle - -### Startup -The `IDestinationResolver` should be registered in the DI container as a singleton. At startup, the proxy will resolve this instance and call `ResolveDestinationsAsync(...)` with the configured destinations retrieved from the resolved `IProxyConfigProviders`. On this first call the provider may choose to: -- Throw an exception if the provider cannot produce a valid proxy configuration for any reason. This will prevent the application from starting. -- Asynchronously resolve the destinations. This will stop the application from starting until resolved destinations are available. -- Or, it may choose to return an empty `ResolvedDestinationCollection` instance while it resolves destinations in the background. The provider will need to trigger the `IChangeToken` when the configuration is available. - -### Atomicity -The destinations objects and collections supplied to the proxy should be read-only and not modified once they have been handed to the proxy via `GetConfig()`. - -### Reload -If the `IChangeToken` supports `ActiveChangeCallbacks`, once the proxy has processed the initial set of destinations it will register a callback with this token. If the provider does not support callbacks then `HasChanged` will be polled alongside `IProxyConfig` change tokens, every 5 minutes. - -When the provider wants to provide a new set of destinations to the proxy it should: -- Resolve those destinations in the background. - - `ResolvedDestinationCollection` is immutable, so new instances have to be created for any new data. - - Objects for unchanged destinations can be re-used, or new instances can be created. -- Invalidate the `IChangeToken` returned from the previous `ResolveDestinationsAsync` invocation. - -Once the new destinations have been applied, the proxy will register a callback with the new `IChangeToken`. Note if there are multiple reloads signaled in close succession, the proxy may skip some and resolve destinations as soon as it's ready. - -## DNS Destination Resolver - -YARP includes an [IDestinationResolver](xref:Yarp.ReverseProxy.ServiceDiscovery.IDestinationResolver) implementation which expands the set of configured destinations by resolving each host name to one or more IP addresses using DNS, creating a destination for each resolved IP. -The DNS destination resolver can be added to your reverse proxy using the `IReverseProxyBuilder.AddDnsDestinationResolver(Action)` method. -The method accepts an optional delegate to configure the resolver's options, [DnsDestinationResolverOptions](xref:Yarp.ReverseProxy.ServiceDiscovery.DnsDestinationResolverOptions). - -### Example - -```csharp -// Add the DNS destination resolver, restricting results to IPv4 addresses -reverseProxyBuilder.AddDnsDestinationResolver(o => o.AddressFamily = AddressFamily.InterNetwork); -``` - -### Configuration - -The DNS destination resolver's options, [DnsDestinationResolverOptions](xref:Yarp.ReverseProxy.ServiceDiscovery.DnsDestinationResolverOptions), has the following properties: - -#### RefreshPeriod - -The period between requesting a refresh of a resolved name. This defaults to 5 minutes. - -#### AddressFamily - -Optionally, specify an `System.Net.Sockets.AddressFamily` value of `AddressFamily.InterNetwork` or `AddressFamily.InterNetworkV6` to restrict resolved resolution to IPv4 or IPv6 addresses, respectively. The default value, `null`, instructs the resolver to not restrict the address family of the results and to use accept all returned addresses. diff --git a/docs/docfx/articles/dests-health-checks.md b/docs/docfx/articles/dests-health-checks.md deleted file mode 100644 index 1d411858b..000000000 --- a/docs/docfx/articles/dests-health-checks.md +++ /dev/null @@ -1,389 +0,0 @@ -# Destination health checks -In most of the real-world systems, it's expected for their nodes to occasionally experience transient issues and go down completely due to a variety of reasons such as an overload, resource leakage, hardware failures, etc. Ideally, it'd be desirable to completely prevent those unfortunate events from occurring in a proactive way, but the cost of designing and building such an ideal system is generally prohibitively high. However, there is another reactive approach which is cheaper and aimed to minimizing a negative impact failures cause on client requests. The proxy can analyze each nodes health and stop sending client traffic to unhealthy ones until they recover. YARP implements this approach in the form of active and passive destination health checks. They are independent from each other and stored on the relative properties for each destination. Health states are initialized with `Unknown` value which can be later changed to either `Healthy` or `Unhealthy` by the corresponding policies as explained below. - -## Active health checks -YARP can proactively monitor destination health by sending periodic probing requests to designated health endpoints and analyzing responses. That analysis is performed by an active health check policy specified for a cluster and results in the calculation of the new destination health states. In the end, the policy marks each destination as healthy or unhealthy based on the HTTP response code (2xx is considered healthy) and rebuilds the cluster's healthy destination collection. - -There are several cluster-wide configuration settings controlling active health checks that can be set either in the config file or in code. A dedicated health endpoint can also be specified per destination. - -#### File example -```JSON -"Clusters": { - "cluster1": { - "HealthCheck": { - "Active": { - "Enabled": "true", - "Interval": "00:00:10", - "Timeout": "00:00:10", - "Policy": "ConsecutiveFailures", - "Path": "/api/health", - "Query": "?foo=bar" - } - }, - "Metadata": { - "ConsecutiveFailuresHealthPolicy.Threshold": "3" - }, - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10000/" - }, - "cluster1/destination2": { - "Address": "http://localhost:10010/", - "Health": "http://localhost:10020/" - } - } - } -} -``` - -#### Code example -```C# -var clusters = new[] -{ - new ClusterConfig() - { - ClusterId = "cluster1", - HealthCheck = new HealthCheckConfig - { - Active = new ActiveHealthCheckConfig - { - Enabled = true, - Interval = TimeSpan.FromSeconds(10), - Timeout = TimeSpan.FromSeconds(10), - Policy = HealthCheckConstants.ActivePolicy.ConsecutiveFailures, - Path = "/api/health", - Query = "?foo=bar", - } - }, - Metadata = new Dictionary { { ConsecutiveFailuresHealthPolicyOptions.ThresholdMetadataName, "5" } }, - Destinations = - { - { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }, - { "destination2", new DestinationConfig() { Address = "https://localhost:10010", Health = "https://localhost:10010" } } - } - } -}; -``` - -### Configuration -All but one of active health check settings are specified on the cluster level in `Cluster/HealthCheck/Active` section. The only exception is an optional `Destination/Health` element specifying a separate active health check endpoint. The actual health probing URI is constructed as `Destination/Address` (or `Destination/Health` when it's set) + `Cluster/HealthCheck/Active/Path`. - -Active health check settings can also be defined in code via the corresponding types in [Yarp.ReverseProxy.Configuration](xref:Yarp.ReverseProxy.Configuration) namespace mirroring the configuration contract. - -`Cluster/HealthCheck/Active` section and [ActiveHealthCheckConfig](xref:Yarp.ReverseProxy.Configuration.ActiveHealthCheckConfig): - -- `Enabled` - flag indicating whether active health check is enabled for a cluster. Default `false` -- `Interval` - period of sending health probing requests. Default `00:00:15` -- `Timeout` - probing request timeout. Default `00:00:10` -- `Policy` - name of a policy evaluating destinations' active health states. Mandatory parameter -- `Path` - health check path on all cluster's destinations. Default `null`. -- `Query` - health check query on all cluster's destinations. Default `null`. - -`Destination` section and [DestinationConfig](xref:Yarp.ReverseProxy.Configuration.DestinationConfig). - -- `Health` - A dedicated health probing endpoint such as `http://destination:12345/`. Defaults `null` and falls back to `Destination/Address`. - -### Built-in policies -There is currently one built-in active health check policy - `ConsecutiveFailuresHealthPolicy`. It counts consecutive health probe failures and marks a destination as unhealthy once the given threshold is reached. On the first successful response, a destination is marked as healthy and the counter is reset. -The policy parameters are set in the cluster's metadata as follows: - -`ConsecutiveFailuresHealthPolicy.Threshold` - number of consecutively failed active health probing requests required to mark a destination as unhealthy. Default `2`. - -### Design -The main service in this process is [IActiveHealthCheckMonitor](xref:Yarp.ReverseProxy.Health.IActiveHealthCheckMonitor) that periodically creates probing requests via [IProbingRequestFactory](xref:Yarp.ReverseProxy.Health.IProbingRequestFactory), sends them to all [DestinationConfig](xref:Yarp.ReverseProxy.Configuration.DestinationConfig) of each [ClusterConfig](xref:Yarp.ReverseProxy.Configuration.ClusterConfig) with enabled active health checks and then passes all the responses down to a [IActiveHealthCheckPolicy](xref:Yarp.ReverseProxy.Health.IActiveHealthCheckPolicy) specified for a cluster. `IActiveHealthCheckMonitor` doesn't make the actual decision on whether a destination is healthy or not, but delegates this duty to an `IActiveHealthCheckPolicy` specified for a cluster. A policy is called to evaluate the new health states once all probing of all cluster's destination completed. It takes in a [ClusterState](xref:Yarp.ReverseProxy.Model.ClusterState) representing the cluster's dynamic state and a set of [DestinationProbingResult](xref:Yarp.ReverseProxy.Health.DestinationProbingResult) storing cluster's destinations' probing results. Having evaluated a new health state for each destination, the policy calls [IDestinationHealthUpdater](xref:Yarp.ReverseProxy.Health.IDestinationHealthUpdater) to actually update [DestinationHealthState.Active](xref:Yarp.ReverseProxy.Model.DestinationHealthState.Active) values. - -``` --{For each cluster's destination}- -IActiveHealthCheckMonitor <--(Create probing request)--> IProbingRequestFactory - | - V - HttpMessageInvoker <--(Send probe and receive response)--> Destination - | -(Save probing result) - | - V -DestinationProbingResult ---------------{END}--------------- - | -(Evaluate new destination active health states using probing results) - | - V -IActiveHealthCheckPolicy --(New active health states)--> IDestinationHealthUpdater --(Update each destination's)--> DestinationState.Health.Active -``` -There are default built-in implementations for all of the aforementioned components which can also be replaced with custom ones when necessary. - -### Extensibility -There are 2 main extensibility points in the active health check subsystem. - -#### IActiveHealthCheckPolicy -[IActiveHealthCheckPolicy](xref:Yarp.ReverseProxy.Health.IActiveHealthCheckPolicy) analyzes how destinations respond to active health probes sent by `IActiveHealthCheckMonitor`, evaluates new active health states for all the probed destinations, and then call `IDestinationHealthUpdater.SetActive` to set new active health states and rebuild the healthy destination collection based on the updated values. - -The below is a simple example of a custom `IActiveHealthCheckPolicy` marking destination as `Healthy`, if a successful response code was returned for a probe, and as `Unhealthy` otherwise. - -```C# -public class FirstUnsuccessfulResponseHealthPolicy : IActiveHealthCheckPolicy -{ - private readonly IDestinationHealthUpdater _healthUpdater; - - public FirstUnsuccessfulResponseHealthPolicy(IDestinationHealthUpdater healthUpdater) - { - _healthUpdater = healthUpdater; - } - - public string Name => "FirstUnsuccessfulResponse"; - - public void ProbingCompleted(ClusterState cluster, IReadOnlyList probingResults) - { - if (probingResults.Count == 0) - { - return; - } - - var newHealthStates = new NewActiveDestinationHealth[probingResults.Count]; - for (var i = 0; i < probingResults.Count; i++) - { - var response = probingResults[i].Response; - var newHealth = response is not null && response.IsSuccessStatusCode ? DestinationHealth.Healthy : DestinationHealth.Unhealthy; - newHealthStates[i] = new NewActiveDestinationHealth(probingResults[i].Destination, newHealth); - } - - _healthUpdater.SetActive(cluster, newHealthStates); - } -} -``` - -#### IProbingRequestFactory -[IProbingRequestFactory](xref:Yarp.ReverseProxy.Health.IProbingRequestFactory) creates active health probing requests to be sent to destination health endpoints. It can take into account `ActiveHealthCheckOptions.Path`, `DestinationConfig.Health`, and other configuration settings to construct probing requests. - -The default `IProbingRequestFactory` uses the same `HttpRequest` configuration as proxy requests, to customize that implement your own `IProbingRequestFactory` and register it in DI like the below. - -```C# -services.AddSingleton(); -``` - -The below is a simple example of a customer `IProbingRequestFactory` concatenating `DestinationConfig.Address` and a fixed health probe path to create the probing request URI. - -```C# -public class CustomProbingRequestFactory : IProbingRequestFactory -{ - public HttpRequestMessage CreateRequest(ClusterConfig clusterConfig, DestinationConfig destinationConfig) - { - var probeUri = new Uri(destinationConfig.Address + "/api/probe-health"); - return new HttpRequestMessage(HttpMethod.Get, probeUri) { Version = ProtocolHelper.Http11Version }; - } -} -``` - -## Passive health checks -YARP can passively watch for successes and failures in client request proxying to reactively evaluate destination health states. Responses to the proxied requests are intercepted by a dedicated passive health check middleware which passes them to a policy configured on the cluster. The policy analyzes the responses to evaluate if the destinations that produced them are healthy or not. Then, it calculates and assigns new passive health states to the respective destinations and rebuilds the cluster's healthy destination collection. - -Note the response is normally sent to the client before the passive health policy runs, so a policy cannot intercept the response body, nor modify anything in the response headers unless the proxy application introduces full response buffering. - -There is one important difference from the active health check logic. Once a destination gets assigned an unhealthy passive state, it stops receiving all new traffic which blocks future health reevaluation. The policy also schedules a destination reactivation after the configured period. Reactivation means resetting the passive health state from `Unhealthy` back to the initial `Unknown` value which makes destination eligible for traffic again. - -There are several cluster-wide configuration settings controlling passive health checks that can be set either in the config file or in code. - -#### File example -```JSON -"Clusters": { - "cluster1": { - "HealthCheck": { - "Passive": { - "Enabled": "true", - "Policy": "TransportFailureRate", - "ReactivationPeriod": "00:02:00" - } - }, - "Metadata": { - "TransportFailureRateHealthPolicy.RateLimit": "0.5" - }, - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10000/" - }, - "cluster1/destination2": { - "Address": "http://localhost:10010/" - } - } - } -} -``` - -#### Code example -```C# -var clusters = new[] -{ - new ClusterConfig() - { - ClusterId = "cluster1", - HealthCheck = new HealthCheckConfig - { - Passive = new PassiveHealthCheckConfig - { - Enabled = true, - Policy = HealthCheckConstants.PassivePolicy.TransportFailureRate, - ReactivationPeriod = TimeSpan.FromMinutes(2) - } - }, - Metadata = new Dictionary { { TransportFailureRateHealthPolicyOptions.FailureRateLimitMetadataName, "0.5" } }, - Destinations = - { - { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }, - { "destination2", new DestinationConfig() { Address = "https://localhost:10010" } } - } - } -}; -``` - -### Configuration -Passive health check settings are specified on the cluster level in `Cluster/HealthCheck/Passive` section. Alternatively, they can be defined in code via the corresponding types in [Yarp.ReverseProxy.Configuration](xref:Yarp.ReverseProxy.Configuration) namespace mirroring the configuration contract. - -Passive health checks require the `PassiveHealthCheckMiddleware` added into the pipeline for them to work. The default `MapReverseProxy(this IEndpointRouteBuilder endpoints)` method does it automatically, but in case of a manual pipeline construction the [UsePassiveHealthChecks](xref:Microsoft.AspNetCore.Builder.AppBuilderHealthExtensions) method must be called to add that middleware as shown in the example below. - -```C# -endpoints.MapReverseProxy(proxyPipeline => -{ - proxyPipeline.UseAffinitizedDestinationLookup(); - proxyPipeline.UseProxyLoadBalancing(); - proxyPipeline.UseRequestAffinitizer(); - proxyPipeline.UsePassiveHealthChecks(); -}); -``` - -`Cluster/HealthCheck/Passive` section and [PassiveHealthCheckConfig](xref:Yarp.ReverseProxy.Configuration.PassiveHealthCheckConfig): - -- `Enabled` - flag indicating whether passive health check is enabled for a cluster. Default `false` -- `Policy` - name of a policy evaluating destinations' passive health states. Mandatory parameter -- `ReactivationPeriod` - period after which an unhealthy destination's passive health state is reset to `Unknown` and it starts receiving traffic again. Default value is `null` which means the period will be set by a `IPassiveHealthCheckPolicy` - -### Built-in policies -There is currently one built-in passive health check policy - [`TransportFailureRateHealthPolicy`](xref:Yarp.ReverseProxy.Health.TransportFailureRateHealthPolicyOptions). It calculates the proxied requests failure rate for each destination and marks it as unhealthy if the specified limit is exceeded. Rate is calculated as a percentage of failed requests to the total number of request proxied to a destination in the given period of time. Failed and total counters are tracked in a sliding time window which means that only the recent readings fitting in the window are taken into account. -There are two sets of policy parameters defined globally and on per cluster level. - -Global parameters are set via the options mechanism using `TransportFailureRateHealthPolicyOptions` type with the following properties: - -- `DetectionWindowSize` - period of time while detected failures are kept and taken into account in the rate calculation. Default is `00:01:00`. -- `MinimalTotalCountThreshold` - minimal total number of requests which must be proxied to a destination within the detection window before this policy starts evaluating the destination's health and enforcing the failure rate limit. Default is `10`. -- `DefaultFailureRateLimit` - default failure rate limit for a destination to be marked as unhealthy that is applied if it's not set on a cluster's metadata. The value is in range `(0,1)`. Default is `0.3` (30%). - -Global policy options can be set in code as follows: -```C# -services.Configure(o => -{ - o.DetectionWindowSize = TimeSpan.FromSeconds(30); - o.MinimalTotalCountThreshold = 5; - o.DefaultFailureRateLimit = 0.5; -}); -``` - -Cluster-specific parameters are set in the cluster's metadata as follows: -`TransportFailureRateHealthPolicy.RateLimit` - failure rate limit for a destination to be marked as unhealthy. The value is in range `(0,1)`. Default value is provided by the global `DefaultFailureRateLimit` parameter. - -### Design -The main component is [PassiveHealthCheckMiddleware](xref:Yarp.ReverseProxy.Health.PassiveHealthCheckMiddleware) sitting in the request pipeline and analyzing responses returned by destinations. For each response from a destination belonging to a cluster with enabled passive health checks, `PassiveHealthCheckMiddleware` invokes an [IPassiveHealthCheckPolicy](xref:Yarp.ReverseProxy.Health.IPassiveHealthCheckPolicy) specified for the cluster. The policy analyzes the given response, evaluates a new destination's passive health state and calls [IDestinationHealthUpdater](xref:Yarp.ReverseProxy.Health.IDestinationHealthUpdater) to actually update [DestinationHealthState.Passive](xref:Yarp.ReverseProxy.Model.DestinationHealthState.Passive) value. The update happens asynchronously in the background and doesn't block the request pipeline. When a destination gets marked as unhealthy, it stops receiving new requests until it gets reactivated after a configured period. Reactivation means the destination's `DestinationHealthState.Passive` state is reset from `Unhealthy` to `Unknown` and the cluster's list of healthy destinations is rebuilt to include it. A reactivation is scheduled by `IDestinationHealthUpdater` right after setting the destination's `DestinationHealthState.Passive` to `Unhealthy`. - -``` - (Response to a proxied request) - | - PassiveHealthCheckMiddleware - | - V - IPassiveHealthCheckPolicy - | - (Evaluate new passive health state) - | - IDestinationHealthUpdater --(Asynchronously update passive state)--> DestinationState.Health.Passive - | - V - (Schedule a reactivation) --(Set to Unknown)--> DestinationState.Health.Passive -``` - -### Extensibility -There is one main extensibility point in the passive health check subsystem, the `IPassiveHealthCheckPolicy`. - -#### IPassiveHealthCheckPolicy -[IPassiveHealthCheckPolicy](xref:Yarp.ReverseProxy.Health.IPassiveHealthCheckPolicy) analyzes how a destination responded to a proxied client request, evaluates its new passive health state and finally calls `IDestinationHealthUpdater.SetPassiveAsync` to create an async task actually updating the passive health state and rebuilding the healthy destination collection. - -The below is a simple example of a custom `IPassiveHealthCheckPolicy` marking destination as `Unhealthy` on the first unsuccessful response to a proxied request. - -```C# -public class FirstUnsuccessfulResponseHealthPolicy : IPassiveHealthCheckPolicy -{ - private static readonly TimeSpan _defaultReactivationPeriod = TimeSpan.FromSeconds(60); - private readonly IDestinationHealthUpdater _healthUpdater; - - public FirstUnsuccessfulResponseHealthPolicy(IDestinationHealthUpdater healthUpdater) - { - _healthUpdater = healthUpdater; - } - - public string Name => "FirstUnsuccessfulResponse"; - - public void RequestProxied(HttpContext context, ClusterState cluster, DestinationState destination) - { - var error = context.Features.Get(); - if (error is not null) - { - var reactivationPeriod = cluster.Model.Config.HealthCheck?.Passive?.ReactivationPeriod ?? _defaultReactivationPeriod; - _healthUpdater.SetPassive(cluster, destination, DestinationHealth.Unhealthy, reactivationPeriod); - } - } -} -``` - -## Available destination collection -Destinations health state is used to determine which of them are eligible for receiving proxied requests. Each cluster maintains its own list of available destinations on `AvailableDestinations` property of the [ClusterDestinationState](xref:Yarp.ReverseProxy.Model.ClusterDestinationsState) type. That list gets rebuilt when any destination's health state changes. The [IClusterDestinationsUpdater](xref:Yarp.ReverseProxy.Health.IClusterDestinationsUpdater) controls that process and calls an [IAvailableDestinationsPolicy](xref:Yarp.ReverseProxy.Health.IAvailableDestinationsPolicy) configured on the cluster to actually choose the available destinations from the all cluster's destinations. There are the following built-in policies provided and custom ones can be implemented if necessary. - -- `HealthyAndUnknown` - inspects each `DestinationState` and adds it on the available destination list if all of the following statements are TRUE. If no destinations are available then requests will get a 503 error. - - Active health checks are disabled on the cluster OR `DestinationHealthState.Active != DestinationHealth.Unhealthy` - - Passive health checks are disabled on the cluster OR `DestinationHealthState.Passive != DestinationHealth.Unhealthy` -- `HealthyOrPanic` - calls `HealthyAndUnknown` policy at first to get the available destinations. If none of them are returned from this call, it marks all cluster's destinations as available. This is the default policy. - -**NOTE**: An available destination policy configured on a cluster will be always called regardless of if any health check is enabled on the given cluster. The health state of a disabled health check is set to `Unknown`. - -### Configuration -#### File example -```JSON -"Clusters": { - "cluster1": { - "HealthCheck": { - "AvailableDestinationsPolicy": "HealthyOrPanic", - "Passive": { - "Enabled": "true" - } - }, - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10000/" - }, - "cluster1/destination2": { - "Address": "http://localhost:10010/" - } - } - } -} -``` - -#### Code example -```C# -var clusters = new[] -{ - new ClusterConfig() - { - ClusterId = "cluster1", - HealthCheck = new HealthCheckConfig - { - AvailableDestinationsPolicy = HealthCheckConstants.AvailableDestinations.HealthyOrPanic, - Passive = new PassiveHealthCheckConfig - { - Enabled = true - } - }, - Destinations = - { - { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }, - { "destination2", new DestinationConfig() { Address = "https://localhost:10010" } } - } - } -}; -``` diff --git a/docs/docfx/articles/diagnosing-yarp-issues.md b/docs/docfx/articles/diagnosing-yarp-issues.md deleted file mode 100644 index 4e263bba1..000000000 --- a/docs/docfx/articles/diagnosing-yarp-issues.md +++ /dev/null @@ -1,239 +0,0 @@ -# Diagnosing YARP-based proxies - -When using a reverse proxy, there is an additional hop from the client to the proxy, and then from the proxy to destination for things to go wrong. This topic should provide some hints and tips for how to debug and diagnose issues when they occur. It assumes that the proxy is already running, and so does not include problems at startup such as configuration errors. - -## Logging -The first step to being able to tell what is going on with YARP is to turn on [logging](https://docs.microsoft.com/aspnet/core/fundamentals/logging/#configure-logging). This is a configuration flag so can be changed on the fly. YARP is implemented as a middleware component for ASP.NET Core, so you need to enable logging for both YARP and ASP.NET to get the complete picture of what is going on. - -By default ASP.NET will log to the console, and the configuration file can be used to control the level of logging. - -```Json - //Sets the Logging level for ASP.NET - "Logging": { - "LogLevel": { - "Default": "Information", - // Uncomment to hide diagnostic messages from runtime and proxy - // "Microsoft": "Warning", - // "Yarp" : "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, -``` - -You want logging infomation from the "Microsoft.AspNetCore.\*" and "Yarp.ReverseProxy.\*" providers. The example above will emit "Information" level events from both providers to the Console. Changing the level to "Debug" will show additional entries. ASP.NET implements change detection for configuration files, so you can edit the appsettings.json file (or appsettings.development.json if running from Visual Studio) while the project is running and observe changes to the log output. - -> Note: Settings in the appsettings.development.json file will override settings in appsettings.json when running in development, so make sure that if you are editing appsettings.json that the values are not overridden. - -### Understanding Log entries - -The logging output is directly tied to the way that ASP.NET Core processes requests. It's important to realize that as middleware, YARP is relying on much of the ASP.NET functionality to process the requests, for example the following is for the processing of a request with "Debug" mode enabled: - -| Level | Log Message | Description | -| ----- | ----------- | ----------- | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[39]
Connection id "0HMCD0JK7K51U" accepted.| Connections are independent of requests, so this is a new connection | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[1]
Connection id "0HMCD0JK7K51U" started. | | -| info | Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5000/ - - | This is the incomming request to ASP.NET | -| dbug | Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
Wildcard detected, all requests with hosts will be allowed. | My configuation does not tie endpoints to specific hostnames | -| dbug | Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
1 candidate(s) found for the request path '/' | This shows what possible matches there are for the route | -| dbug | Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
Endpoint 'minimumroute' with route pattern '{\*\*catch-all}' is valid for the request path '/' | The mimimum route from YARPs configuration has matched| -| dbug | Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
Request matched endpoint 'minimumroute' | | -| info | Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'minimumroute' | | -| info | Yarp.ReverseProxy.Forwarder.HttpForwarder[9]
Proxying to http://www.example.com/ | YARP is proxying the request to example.com | -| info | Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'minimumroute' | | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[9]
Connection id "0HMCD0JK7K51U" completed keep alive response. | The response has finished, but connection can be kept alive. | -| info | Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://localhost:5000/ - - - 200 1256 text/html;+charset=utf-8 12.7797ms| The response completed with status code 200, responding with 1256 bytes as text/html in ~13ms. | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
Connection id "0HMCD0JK7K51U" received FIN. | Diagnostic information about the connection to determine who closed it and how cleanly | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[10]
Connection id "0HMCD0JK7K51U" disconnecting. | | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[2]
Connection id "0HMCD0JK7K51U" stopped. | | -| dbug | Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
Connection id "0HMCD0JK7K51U" sending FIN because: "The Socket transport's send loop completed gracefully." | | - -The above gives general information about the request and how it was processed. - -### Using ASP.NET Request Logging - -ASP.NET includes a middleware component that can be used to provide more details about the request and response. The `UseHttpLogging` component can be added to the request pipeline. It will add additional entries to the log detailing the incoming and outgoing request headers. - -``` C# -app.UseHttpLogging(); -// Enable endpoint routing, required for the reverse proxy -app.UseRouting(); -// Register the reverse proxy routes -app.MapReverseProxy(); -``` - -For example: - -```Console -info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1] - Request: - Protocol: HTTP/1.1 - Method: GET - Scheme: http - PathBase: - Path: / - Accept: */* - Host: localhost:5000 - User-Agent: curl/7.55.1 -info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2] - Response: - StatusCode: 200 - Content-Type: text/html; charset=utf-8 - Date: Tue, 12 Oct 2021 23:29:20 GMT - Server: ECS,(sec/97A5) - Age: 113258 - Cache-Control: [Redacted] - ETag: [Redacted] - Expires: Tue, 19 Oct 2021 23:29:20 GMT - Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT - Vary: [Redacted] - Content-Length: 1256 - X-Cache: [Redacted] -``` - -## Using Telemetry Events - -We recommend reading https://learn.microsoft.com/dotnet/fundamentals/networking/networking-telemetry as a primer on how to consume networking telemetry in .NET. - -The [Metrics sample](https://github.com/microsoft/reverse-proxy/tree/main/samples/ReverseProxy.Metrics.Sample) shows how to listen to events from the different providers that collect telemetry as part of YARP. The most important from a diagnostics perspective are: - -* `ForwarderTelemetryConsumer` -* `HttpClientTelemetryConsumer` - -To use either of these you create a class implementing a [telemetry interface](https://microsoft.github.io/reverse-proxy/api/Yarp.Telemetry.Consumption.html#interfaces), such as [`IForwarderTelemetryConsumer`](https://github.com/microsoft/reverse-proxy/blob/release/latest/src/TelemetryConsumption/Forwarder/IForwarderTelemetryConsumer.cs): - -```C# -public class ForwarderTelemetry : IForwarderTelemetryConsumer -{ - /// Called before forwarding a request. - public void OnForwarderStart(DateTime timestamp, string destinationPrefix) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStart :: Destination prefix: {destinationPrefix}"); - } - - /// Called after forwarding a request. - public void OnForwarderStop(DateTime timestamp, int statusCode) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStop :: Status: {statusCode}"); - } - - /// Called before if forwarding the request failed. - public void OnForwarderFailed(DateTime timestamp, ForwarderError error) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderFailed :: Error: {error.ToString()}"); - } - - /// Called when reaching a given stage of forwarding a request. - public void OnForwarderStage(DateTime timestamp, ForwarderStage stage) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStage :: Stage: {stage.ToString()}"); - } - - /// Called periodically while a content transfer is active. - public void OnContentTransferring(DateTime timestamp, bool isRequest, long contentLength, long iops, TimeSpan readTime, TimeSpan writeTime) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnContentTransferring :: Is request: {isRequest}, Content length: {contentLength}, IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}"); - } - - /// Called after transferring the request or response content. - public void OnContentTransferred(DateTime timestamp, bool isRequest, long contentLength, long iops, TimeSpan readTime, TimeSpan writeTime, TimeSpan firstReadTime) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnContentTransferred :: Is request: {isRequest}, Content length: {contentLength}, IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}"); - } - - /// Called before forwarding a request from `ForwarderMiddleware`, therefore is not called for direct forwarding scenarios. - public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId, string destinationId) - { - Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderInvoke:: Cluster id: {clusterId}, Route Id: { routeId}, Destination: {destinationId}"); - } -} -``` - -And then register the class as part of services, for example: - -```C# -services.AddTelemetryConsumer(); - -// Add the reverse proxy to capability to the server -var proxyBuilder = services.AddReverseProxy(); -// Initialize the reverse proxy from the "ReverseProxy" section of configuration -proxyBuilder.LoadFromConfig(Configuration.GetSection("ReverseProxy")); -``` - -Then you will log details on each part of the request, for example: - -```Console -Forwarder Telemetry [06:40:48.186] => OnForwarderInvoke:: Cluster id: minimumcluster, Route Id: minimumroute, Destination: example.com -Forwarder Telemetry [06:41:00.269] => OnForwarderStart :: Destination prefix: http://www.example.com/ -Forwarder Telemetry [06:41:00.298] => OnForwarderStage :: Stage: SendAsyncStart -Forwarder Telemetry [06:41:00.507] => OnForwarderStage :: Stage: SendAsyncStop -Forwarder Telemetry [06:41:00.530] => OnForwarderStage :: Stage: ResponseContentTransferStart -Forwarder Telemetry [06:41:03.655] => OnForwarderStop :: Status: 200 -``` - -The events for Telemetry are fired as they occur, so you can [fish out the HttpContext](https://docs.microsoft.com/aspnet/core/fundamentals/http-context#use-httpcontext-from-custom-components) and the YARP feature from it: - -``` C# -services.AddTelemetryConsumer(); -services.AddHttpContextAccessor(); -... -public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId, string destinationId) -{ - var context = new HttpContextAccessor().HttpContext; - var YarpFeature = context.GetReverseProxyFeature(); - - var dests = from d in YarpFeature.AvailableDestinations - select d.Model.Config.Address; - - Console.WriteLine($"Destinations: {string.Join(", ", dests)}"); -} -``` - -## Using custom middleware - -Another way to inspect the state for requests is to insert additional middleware into the request pipeline. You can insert between the other stages to see the state of the request. - -```C# -// We can customize the proxy pipeline and add/remove/replace steps -app.MapReverseProxy(proxyPipeline => -{ - // Use a custom proxy middleware, defined below - proxyPipeline.Use(MyCustomProxyStep); - // Don't forget to include these two middleware when you make a custom proxy pipeline (if you need them). - proxyPipeline.UseSessionAffinity(); - proxyPipeline.UseLoadBalancing(); -}); -... -public Task MyCustomProxyStep(HttpContext context, Func next) -{ - // Can read data from the request via the context - foreach (var header in context.Request.Headers) - { - Console.WriteLine($"{header.Key}: {header.Value}"); - } - - // The context also stores a ReverseProxyFeature which holds proxy specific data such as the cluster, route and destinations - var proxyFeature = context.GetReverseProxyFeature(); - Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(proxyFeature.Route.Config)); - - // Important - required to move to the next step in the proxy pipeline - return next(); -} -``` - -You can also use [ASP.NET middleware](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/write) within Configure that will enable you to inspect the request before the proxy pipeline. - -> **Note:** The proxy will stream the response from the destination server back to the client, so the response headers and body are not readily accessible via middleware. - -## Using the debugger - -A debugger, such as Visual Studio can be attached to the proxy process. However, unless you have existing middleware, there is not a good place in the app code to break and inspect the state of the request. Therefore the debugger is best used in conjunction with one of the techniques above so that you have distinct places to insert breakpoints etc. - -## Network Tracing - -It can be attractive to use network tracing tools like [Fiddler](https://www.telerik.com/fiddler) or [Wireshark](https://www.wireshark.org/) to try to monitor what is happening either side of the proxy. However there are some gotchas to using both tools. - -- Fiddler registers itself as a proxy and relies on applications using the default proxy to be able to monitor traffic. This works for inbound traffic from a browser to YARP, but will not capture the outbound requests as YARP is configured to not use the proxy settings for outbound traffic. -- On windows Wireshark uses Npcap to capture packet data for network traffic, so it will capture both inbound and outbound traffic, and can be used to monitor HTTP traffic. Wireshark works out of the box for HTTP traffic. -- HTTPS traffic is encrypted, and so is not automatically decryptable by network monitoring tools. Each tool has workarounds that may enable traffic to be monitored, but they require hacking around with certificates and trust relationships. Because YARP is making outbound requests, techniques for tricking browsers do not apply to the YARP process. - -The protocol choice for outbound traffic is made based on the destination URL in the cluster configuration. If traffic monitoring is being used for diagnostics then, if possible, changing the outbound URLs to "http://" may be simplest approach to enable the monitoring tools to work, provided the issues being diagnosed are not transport protocol related. diff --git a/docs/docfx/articles/direct-forwarding.md b/docs/docfx/articles/direct-forwarding.md deleted file mode 100644 index f227def6f..000000000 --- a/docs/docfx/articles/direct-forwarding.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -uid: direct-forwarding -title: Direct Forwarding ---- - -# Direct Forwarding - -Some applications only need the ability to take a specific request and forward it to a specific destination. These applications do not need, or have addressed in other ways, the other features of the proxy like configuration discovery, routing, load balancing, etc.. - -## IHttpForwarder - -[IHttpForwarder](xref:Yarp.ReverseProxy.Forwarder.IHttpForwarder) serves as the core proxy adapter between incoming AspNetCore and outgoing System.Net.Http requests. It handles the mechanics of creating a HttpRequestMessage from a HttpContext, sending it, and relaying the response. - -IHttpForwarder supports: -- Dynamic destination selection, you specify the destination for each request -- Http client customization, you provide the HttpMessageInvoker -- Request and response customization (except bodies) -- Streaming protocols like gRPC and WebSockets -- Error handling - -It does not include: -- Routing -- Load balancing -- Affinity -- Retries - -## Example - -See [ReverseProxy.Direct.Sample](https://github.com/microsoft/reverse-proxy/tree/release/latest/samples/ReverseProxy.Direct.Sample) as a pre-built sample, or use the steps below. - -### Create a new project - -Follow the [Getting Started](xref:getting-started) guide to create a project and add the Yarp.ReverseProxy nuget dependency. - -### Update Program.cs - -In this example the IHttpForwarder is registered in DI, injected into the endpoint method, and used to forward requests from a specific route to `https://localhost:10000/prefix/`. - -The optional transforms show how to copy all request headers except for the `Host`, it's common that the destination requires its own `Host` from the url. - -```C# -using System; -using System.Diagnostics; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using System.Threading; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Yarp.ReverseProxy.Forwarder; -using Yarp.ReverseProxy.Transforms; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddHttpForwarder(); - -var app = builder.Build(); - -// Configure our own HttpMessageInvoker for outbound calls for proxy operations -var httpClient = new HttpMessageInvoker(new SocketsHttpHandler -{ - UseProxy = false, - AllowAutoRedirect = false, - AutomaticDecompression = DecompressionMethods.None, - UseCookies = false, - EnableMultipleHttp2Connections = true, - ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), - ConnectTimeout = TimeSpan.FromSeconds(15), -}); - -// Setup our own request transform class -var transformer = new CustomTransformer(); // or HttpTransformer.Default; -var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) }; - -app.UseRouting(); - -// When using IHttpForwarder for direct forwarding you are responsible for routing, destination discovery, load balancing, affinity, etc.. -// For an alternate example that includes those features see BasicYarpSample. -app.Map("/test/{**catch-all}", async (HttpContext httpContext, IHttpForwarder forwarder) => -{ - var error = await forwarder.SendAsync(httpContext, "https://localhost:10000/", - httpClient, requestConfig, transformer); - // Check if the operation was successful - if (error != ForwarderError.None) - { - var errorFeature = httpContext.GetForwarderErrorFeature(); - var exception = errorFeature.Exception; - } -}); - -app.Run(); - -/// -/// Custom request transformation -/// -internal class CustomTransformer : HttpTransformer -{ - /// - /// A callback that is invoked prior to sending the proxied request. All HttpRequestMessage - /// fields are initialized except RequestUri, which will be initialized after the - /// callback if no value is provided. The string parameter represents the destination - /// URI prefix that should be used when constructing the RequestUri. The headers - /// are copied by the base implementation, excluding some protocol headers like HTTP/2 - /// pseudo headers (":authority"). - /// - /// The incoming request. - /// The outgoing proxy request. - /// The uri prefix for the selected destination server which can be used to create - /// the RequestUri. - public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken) - { - // Copy all request headers - await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); - - // Customize the query string: - var queryContext = new QueryTransformContext(httpContext.Request); - queryContext.Collection.Remove("param1"); - queryContext.Collection["area"] = "xx2"; - - // Assign the custom uri. Be careful about extra slashes when concatenating here. RequestUtilities.MakeDestinationAddress is a safe default. - proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://example.com", httpContext.Request.Path, queryContext.QueryString); - - // Suppress the original request header, use the one from the destination Uri. - proxyRequest.Headers.Host = null; - } -} -``` - -```C# -private class CustomTransformer : HttpTransformer -{ - public override async ValueTask TransformRequestAsync(HttpContext httpContext, - HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken) - { - // Copy all request headers - await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); - - // Customize the query string: - var queryContext = new QueryTransformContext(httpContext.Request); - queryContext.Collection.Remove("param1"); - queryContext.Collection["area"] = "xx2"; - - // Assign the custom uri. Be careful about extra slashes when concatenating here. RequestUtilities.MakeDestinationAddress is a safe default. - proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://example.com", httpContext.Request.Path, queryContext.QueryString); - - // Suppress the original request header, use the one from the destination Uri. - proxyRequest.Headers.Host = null; - } -} -``` - -There are also [extension methods](xref:Microsoft.AspNetCore.Builder.DirectForwardingIEndpointRouteBuilderExtensions) available that simplify the mapping of IHttpForwarder to endpoints. - -```C# -app.MapForwarder("/{**catch-all}", "https://localhost:10000/", requestConfig, transformer, httpClient); -``` - -### The HTTP Client - -The http client may be customized, but the above example is recommended for common proxy scenarios. - -Always use HttpMessageInvoker rather than HttpClient, HttpClient buffers responses by default. Buffering breaks streaming scenarios and increases memory usage and latency. - -Re-using a client for requests to the same destination is recommended for performance reasons as it allows you to re-use pooled connections. A client may also be re-used for requests to different destinations if the configuration is the same. - -### Transforms - -The request and response can be modified by providing a derived [HttpTransformer](xref:Yarp.ReverseProxy.Forwarder.HttpTransformer) as a parameter to [`SendAsync`](xref:Yarp.ReverseProxy.Forwarder.IHttpForwarder) method. - -### Error handling - -IHttpForwarder catches exceptions and timeouts from the HTTP client, logs them, and converts them to 5xx status codes or aborts the response. An error code is returned from `SendAsync`, and the error details can be accessed from the [IForwarderErrorFeature](xref:Yarp.ReverseProxy.Forwarder.IForwarderErrorFeature) as shown above. diff --git a/docs/docfx/articles/distributed-tracing.md b/docs/docfx/articles/distributed-tracing.md deleted file mode 100644 index 8c7d7c57e..000000000 --- a/docs/docfx/articles/distributed-tracing.md +++ /dev/null @@ -1,135 +0,0 @@ - -# Distributed tracing - -As an ASP.NET Core component, YARP can easily integrate into different tracing systems the same as any other ASP.NET Core application. - -.NET has built-in configurable support for distributed tracing that YARP takes advantage of to enable such scenarios out-of-the-box. - -## Using Open Telemetry - -YARP supports distributed tracing using Open Telemetry (OTEL). When a request comes in, and there is a listener for Activities, then ASP.NET Core will propagate the [Trace Context](https://www.w3.org/TR/trace-context) trace-id, or create one if necessary, and create new spans/activities for the work performed. -In addition YARP can create activities for: - -- Forwarding Requests -- Active health checks for clusters - -These will only be created if there is a listener for the [`ActivitySource`](https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#activitysource) named `Yarp.ReverseProxy`. - -### Example: Application Insights - -For example, to monitor the traces with Application Insights, the proxy application needs to use the [Open Telemetry](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry/README.md) and [Azure Monitor](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/README.md) SDKs. - -`application.csproj`: - -``` xml - - - -``` - -`Program.cs`: - -``` c# -using Azure.Monitor.OpenTelemetry.AspNetCore; -using OpenTelemetry.Trace; -using System.Diagnostics; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); - -builder.Services.AddOpenTelemetry() - // Use helper to configure Azure Monitor defaults - .UseAzureMonitor(o => - { - o.ConnectionString = builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]; - }) - .WithTracing(t => - { - // Listen to the YARP tracing activities - t.AddSource("Yarp.ReverseProxy"); - }); - -var app = builder.Build(); - -app.MapReverseProxy(); - -app.Run(); - -``` - -### Example: OpenTelemetry hosting - -``` xml - - - - - - - - -``` - -``` csharp -using OpenTelemetry.Logs; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); - -// configure OTel and OTLP -const string serviceName = "yarpProxy"; - -builder.Logging.AddOpenTelemetry(options => -{ - options - .SetResourceBuilder( - ResourceBuilder.CreateDefault() - .AddService(serviceName)) - .AddOtlpExporter(); -}); - -builder.Services.AddOpenTelemetry() - .ConfigureResource(resource => resource.AddService(serviceName)) - .WithTracing(tracing => tracing - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddSource("Yarp.ReverseProxy") - .AddOtlpExporter() - ); - -// build and start app -var app = builder.Build(); -app.MapReverseProxy(); -app.Run(); -``` - -Note that the `AddHttpClientInstrumentation()` call is required along with the `AddSource("Yarp.ReverseProxy")` call to make the request spans emit. - -See [ASP.NET Documentation on Observability with OpenTelemetry](https://learn.microsoft.com/dotnet/core/diagnostics/observability-with-otel). - - -Provided that the traces are being logged to the same store for the proxy and destination servers, then the tracing analysis tools can correlate the requests and provide gant charts etc covering the end-to-end processing of the requests as they transition across the servers. - -The same pattern can be used with the built-in OTEL exporters for Jaeger and Zipkin, or with many of the [APM vendors](https://opentelemetry.io/ecosystem/vendors/) who are adopting OTEL. - -## Using custom tracing headers - -When using a propagation mechanism that is not built into .NET (e.g. [B3 propagation]), you should implement a custom [`DistributedContextPropagator`] for that scheme. - -YARP will remove any header in [`DistributedContextPropagator.Fields`] so that the propagator may re-add them to the request during the `Inject` call. - -## Pass-through proxy - -If you do not wish the proxy to actively participate in the trace, and wish to keep all the tracing headers as-is, you may do so by setting `SocketsHttpHandler.ActivityHeadersPropagator` to `null`. - -```c# -services.AddReverseProxy() - .ConfigureHttpClient((context, handler) => handler.ActivityHeadersPropagator = null); -``` - -[B3 propagation]: https://github.com/openzipkin/b3-propagation -[`DistributedContextPropagator`]: https://docs.microsoft.com/dotnet/api/system.diagnostics.distributedcontextpropagator -[`DistributedContextPropagator.Fields`]: https://docs.microsoft.com/dotnet/api/system.diagnostics.distributedcontextpropagator.fields diff --git a/docs/docfx/articles/getting-started.md b/docs/docfx/articles/getting-started.md deleted file mode 100644 index 029eae1a7..000000000 --- a/docs/docfx/articles/getting-started.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -uid: getting-started -title: Getting Started with YARP ---- - -# Getting Started with YARP - -YARP is designed as a library that provides the core proxy functionality which you can then customize by adding or replacing modules. -YARP is currently provided as a NuGet package and code snippets. -We plan on providing a project template and pre-built exe in the future. - -YARP is implemented on top of .NET Core infrastructure and is usable on Windows, Linux or MacOS. -Development can be done with the SDK and your favorite editor, [Microsoft Visual Studio](https://visualstudio.microsoft.com/vs/) or [Visual Studio Code](https://code.visualstudio.com/). - -YARP 2.1.0 supports ASP.NET Core 6.0 and newer, including ASP.NET Core 8.0. -You can download the .NET SDK from https://dotnet.microsoft.com/download/dotnet/. - -Visual Studio support for .NET 8 is included in Visual Studio 2022 17.8. - -### Create a new project - -A complete version of the project built using the steps below can be found at [Basic YARP Sample](https://github.com/microsoft/reverse-proxy/tree/release/latest/samples/BasicYarpSample). - -Start by creating an "Empty" ASP.NET Core application using the command line: - -```Console -dotnet new web -n MyProxy -f net8.0 -``` - -Or create a new ASP.NET Core web application in Visual Studio 2022, and choose "Empty" for the project template. - -### Add the project reference - - ```XML - - - -``` - -### Add the YARP Middleware - -Update Program.cs to use the YARP middleware: - -```C# -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddReverseProxy() - .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); -var app = builder.Build(); -app.MapReverseProxy(); -app.Run(); -``` - -## Configuration - -The configuration for YARP is defined in the appsettings.json file. See [Configuration Files](config-files.md) for details. - -The configuration can also be provided programmatically. See [Configuration Providers](config-providers.md) for details. - -You can find out more about the available configuration options by looking at [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and [ClusterConfig](xref:Yarp.ReverseProxy.Configuration.ClusterConfig). - - ```JSON - { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*", - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}" - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "destination1": { - "Address": "https://example.com/" - } - } - } - } - } -} -``` - -### Running the project - -Use `dotnet run` called within the sample's directory or `dotnet run --project ` diff --git a/docs/docfx/articles/grpc.md b/docs/docfx/articles/grpc.md deleted file mode 100644 index 661835994..000000000 --- a/docs/docfx/articles/grpc.md +++ /dev/null @@ -1,53 +0,0 @@ -# Proxing gRPC - -## Introduction - -[gRPC](https://grpc.io/) is a language agnostic, high-performance Remote Procedure Call (RPC) framework. It's built on top of HTTP/2 and can be proxied through YARP. While YARP doesn't need to be aware of the gRPC messages, you do need to make sure the right HTTP protocol is enabled. gRPC requires HTTP/2 and gRPC calls will fail if YARP isn't correctly configured to send and receive HTTP/2 requests. - -## Configure YARP's Incoming Protocols - -gRPC requires HTTP/2 for most scenarios. HTTP/1.1 and HTTP/2 are enabled by default on ASP.NET Core servers (YARP's front end) but they require https (TLS) for HTTP/2 so YARP needs to be listening on a `https://` URL. - -HTTP/2 over http (non-TLS) is only supported on Kestrel and requires specific settings. For details see [here](https://docs.microsoft.com/aspnet/core/grpc/aspnetcore#server-options). - -This shows configuring Kestrel to use HTTP/2 over http (non-TLS): -```json -{ - "Kestrel": { - "Endpoints": { - "http": { - "Url": "http://localhost:5000", - "Protocols": "Http2" - } - } - } -} -``` - -## Configure YARP's Outgoing Protocols - -YARP automatically negotiates HTTP/1.1 or HTTP/2 for outgoing proxy requests, but only for https (TLS). HTTP/2 over http (non-TLS) requires additional settings. Note outgoing protocols are independent of incoming ones. E.g. https can be used for the incoming connection and http for the outgoing one, this is called TLS termination. See [here](http-client-config.md#httprequest) for configuration details. - -This shows configuring the outgoing proxy request to use HTTP/2 over http. -```json -"cluster1": { - "HttpRequest": { - "Version": "2", - "VersionPolicy": "RequestVersionExact" - }, - "Destinations": { - "cluster1/destination1": { - "Address": "http://localhost:6000/" - } - } -}, -``` - -## gRPC-Web - -[gRPC-Web](https://grpc.io/docs/platforms/web/basics/) is an alternative wire-format for gRPC that's compatible with HTTP/1.1. - -* [`application/grpc`](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) - gRPC over HTTP/2 is how gRPC is typically used. -* [`application/grpc-web`](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) - gRPC-Web modifies the gRPC protocol to be compatible with HTTP/1.1. gRPC-Web can be used in more places. gRPC-Web can be used by browser apps and in networks without complete support for HTTP/2. Two advanced gRPC features are not supported: client streaming and bidirectional streaming. - -gRPC-Web can be proxied by YARP's default configuration without any special considerations. diff --git a/docs/docfx/articles/header-guidelines.md b/docs/docfx/articles/header-guidelines.md deleted file mode 100644 index c39ec004c..000000000 --- a/docs/docfx/articles/header-guidelines.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -uid: header-guidelines -title: Header Guidelines ---- - -# HTTP Headers - -Headers are a very important part of processing HTTP requests and each have their own semantics and considerations. Most headers are proxied by default, though some used to control how the request is delivered are automatically adjusted or removed by the proxy. The connections between the client and the proxy and the proxy and destination are independent, and so headers that affect the connection and transport need to be filtered. Many headers contain information like domain names, paths, or other details that may be affected when a reverse proxy is included in the application architecture. Below is a collection of guidelines about how specific headers might be impacted and what to do about them. - -## YARP header filtering - -YARP automatically removes request and response headers that could impact its ability to forward a request correctly, or that may be used maliciously to bypass features of the proxy. A complete list can be found [here](https://github.com/microsoft/reverse-proxy/blob/main/src/ReverseProxy/Forwarder/RequestUtilities.cs#L71), with some highlights described below. - -### Connection, KeepAlive, Close - -These headers control how the TCP connection is managed and are removed to avoid impacting the connection on the other side of the proxy. - -### Transfer-Encoding - -This header describes the format of the request or response body on the wire, e.g. 'chunked', and is removed because the format can vary between the internal and external connection. The incoming and outgoing HTTP stacks will add transport headers as needed. - -### TE - -Only the `TE: trailers` header value is allowed through the proxy since it's required for some gRPC implementations. - -### Upgrade - -This is used for protocols like WebSockets. It is removed by default and only added back for specifically supported protocols (WebSockets, SPDY). - -### Proxy-* - -These are headers used with proxies and are not considered appropriate to forward. - -### Alt-Svc - -This response header is used with HTTP/3 upgrades and only applies to the immediate connection. - -### Distributed tracing headers - -These headers include TraceParent, Request-Id, TraceState, Baggage, Correlation-Context. -They are automatically removed based on `DistributedContextPropagator.Fields` so that the forwarding HttpClient can replace them with updated values. -You can opt out of modifying these headers by setting `SocketsHttpHandler.ActivityHeadersPropagator` to `null`: -```C# -services.AddReverseProxy() - .ConfigureHttpClient((_, handler) => handler.ActivityHeadersPropagator = null); -``` - -### Strict-Transport-Security - -This header instructs clients to always use HTTPS, but there may be a conflict between values provided by the proxy and destination. To avoid confusion, the destination's value is not copied to the response if one was already added to the response by the proxy application. - -## Other Header Guidelines - -### Host - -The Host header indicates which site on the server the request is intended for. This header is removed by default since the host name used publicly by the proxy is likely to differ from the one used by the service behind the proxy. This is configurable using the [RequestHeaderOriginalHost](transforms.md#requestheaderoriginalhost) transform. - -### X-Forwarded-*, Forwarded - -Because a separate connection is used to communicate with the destination, these request headers can be used to forward information about the original connection like the IP, scheme, port, client certificate, etc.. X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Prefix are enabled by default. This information is subject to spoofing attacks so any existing headers on the request are removed and replaced by default. The destination app should be careful about how much trust it places in these values. See [transforms](transforms.md#defaults) for configuring these in the proxy. See the [ASP.NET docs](https://docs.microsoft.com/aspnet/core/host-and-deploy/proxy-load-balancer) for configuring the destination app to read these headers. - -### X-http-method-override, x-http-method, x-method-override - -Some clients and servers limit which HTTP methods they allow (GET, POST, etc.). These request headers are sometimes used to work around those restrictions. These headers are proxied by default. If in the proxy you want to prevent these bypasses then use the [RequestHeaderRemove](transforms.md#requestheaderremove) transform. - -### Set-Cookie - -This response header may contain fields constraining the path, domain, scheme, etc. where the cookie should be used. Using a reverse proxy may change the effective domain, path, or scheme of a site from the public view. While it would be possible to [rewrite](https://github.com/microsoft/reverse-proxy/issues/1109) response cookies using custom transforms, it's recommended instead to use the Forwarded headers described above to flow the correct values to the destination app so it can generate the correct set-cookie headers. - -### Location - -This response header is used with redirects and may contain a scheme, domain, and path that differ from the public values due to the use of the proxy. While it would be possible to [rewrite](https://github.com/microsoft/reverse-proxy/discussions/466) the Location header using custom transforms, it's recommended instead to use the Forwarded headers described above to flow the correct values to the destination app so it can generate the correct Location headers. - -### Server - -This response header indicates what server technology was used to generate the response (IIS, Kestrel, etc.). This header is proxied from the destination by default. Applications that want to remove it can use the [ResponseHeaderRemove](transforms.md#responseheaderremove) transform, in which case the proxy's default server header will be used. Suppressing the proxy default server header is server specific, such as for [Kestrel](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.server.kestrel.core.kestrelserveroptions.addserverheader#Microsoft_AspNetCore_Server_Kestrel_Core_KestrelServerOptions_AddServerHeader). - -### X-Powered-By - -This response header indicates what web framework was used to generate the response (ASP.NET, etc.). ASP.NET Core does not generate this header but IIS can. This header is proxied from the destination by default. Applications that want to remove it can use the [ResponseHeaderRemove](transforms.md#responseheaderremove) transform. diff --git a/docs/docfx/articles/header-routing.md b/docs/docfx/articles/header-routing.md deleted file mode 100644 index 892e2b312..000000000 --- a/docs/docfx/articles/header-routing.md +++ /dev/null @@ -1,424 +0,0 @@ ---- -uid: header-routing -title: Header Routing ---- - -# Header Based Routing - -Proxy routes specified in [config](config-files.md) or via [code](config-providers.md) must include at least a path or host to match against. In addition to these, a route can also specify one or more headers that must be present on the request. - -### Precedence - -The default route match precedence order is - -1. path -2. method -3. host -4. headers -5. query parameters - - That means a route which specifies methods and no headers will match before a route which specifies headers and no methods. This can be overridden by setting the `Order` property on a route (see example in [config properties](config-files.md#all-config-properties)). - -## Configuration - -Headers are specified in the `Match` section of a proxy route. - -If multiple headers rules are specified on a route then all must match for a route to be taken. OR logic must be implemented either within a header rule or as separate routes. - -Configuration: -```JSON -"Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header1", - "Values": [ "value1" ], - "Mode": "ExactHeader" - } - ] - } - }, - "route2" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header2", - "Values": [ "1prefix", "2prefix" ], - "Mode": "HeaderPrefix" - } - ] - } - }, - "route3" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header3", - "Mode": "Exists" - } - ] - } - }, - "route4" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header4", - "Values": [ "value1", "value2" ], - "Mode": "ExactHeader" - }, - { - "Name": "header5", - "Mode": "Exists" - } - ] - } - }, - "route5" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header5", - "Values": [ "value1", "value2" ], - "Mode": "Contains" - }, - { - "Name": "header6", - "Mode": "Exists" - } - ] - } - }, - "route6" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header6", - "Values": [ "value1", "value2" ], - "Mode": "NotContains" - }, - { - "Name": "header7", - "Mode": "Exists" - } - ] - } - }, - "route7" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "Headers": [ - { - "Name": "header7", - "Mode": "NotExists" - } - ] - } - } -} -``` - -Code: -```C# -var routes = new[] -{ - new RouteConfig() - { - RouteId = "route1", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header1", - Values = new[] { "value1" }, - Mode = HeaderMatchMode.ExactHeader - } - } - } - }, - new RouteConfig() - { - RouteId = "route2", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header2", - Values = new[] { "1prefix", "2prefix" }, - Mode = HeaderMatchMode.HeaderPrefix - } - } - } - }, - new RouteConfig() - { - RouteId = "route3", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header3", - Mode = HeaderMatchMode.Exists - } - } - } - }, - new RouteConfig() - { - RouteId = "route4", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header4", - Values = new[] { "value1", "value2" }, - Mode = HeaderMatchMode.ExactHeader - }, - new RouteHeader() - { - Name = "Header5", - Mode = HeaderMatchMode.Exists - } - } - } - }, - new RouteConfig() - { - RouteId = "route5", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header5", - Values = new[] { "value1", "value2" }, - Mode = HeaderMatchMode.Contains - } - } - } - }, - new RouteConfig() - { - RouteId = "route6", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header6", - Values = new[] { "value1", "value2" }, - Mode = HeaderMatchMode.NotContains - } - } - } - }, - new RouteConfig() - { - RouteId = "route7", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - Headers = new[] - { - new RouteHeader() - { - Name = "Header7", - Mode = HeaderMatchMode.NotExists - } - } - } - } -}; -``` - -## Contract - -[RouteHeader](xref:Yarp.ReverseProxy.Configuration.RouteHeader) defines the code contract and is mapped from config. - -### Name - -The header name to check for on the request. A non-empty value is required. This field is case-insensitive per the HTTP RFCs. - -### Values - -A list of possible values to search for. The header must match at least one of these values according to the specified `Mode` except for the 'NotContains'. At least one value is required unless `Mode` is set to `Exists` or `NotExists`. - -### Mode - -[HeaderMatchMode](xref:Yarp.ReverseProxy.Configuration.HeaderMatchMode) specifies how to match the value(s) against the request header. The default is `ExactHeader`. -- ExactHeader - Any of the headers with the given name must match in its entirety, subject to the value of `IsCaseSensitive`. If a header contains multiple values (separated by `,` or `;`), they are split before matching. A single pair of quotes will also be stripped from the value before matching. -- HeaderPrefix - Any of the headers with the given name must match by prefix, subject to the value of `IsCaseSensitive`. If a header contains multiple values (separated by `,` or `;`), they are split before matching. A single pair of quotes will also be stripped from the value before matching. -- Exists - The header must exist and contain any non-empty value. If there are multiple headers with the same name, the rule will also match. -- Contains - Any of the headers with the given name must contain any of the match values, subject to the value of `IsCaseSensitive`. -- NotContains - None of the headers with the given name may contain any of the match values, subject to the value of `IsCaseSensitive`. - -### IsCaseSensitive - -Indicates if the value match should be performed as case sensitive or insensitive. The default is `false`, insensitive. - -## Examples - -These examples use the configuration specified above. - -### Scenario 1 - Exact Header Match - -A request with the following header will match against route1. -``` -Header1: Value1 -``` -If a header contains multiple values, each one will be matched separately. The following request will match. -``` -Header1: Value1, Value2 -``` -The same holds if multiple values are split across multiple headers with the same name. -``` -Header1: Value1 -Header1: Value2 -``` -A single pair of enclosing quotes may be stripped from the value before matching. The following request will match. -``` -Header1: "Value1" -``` -Multiple pairs of quotes will _not_ match. -``` -Header1: ""Value1"" -``` - -### Scenario 2 - Multiple Values - -Route2 defined multiple values to search for in a header ("1prefix", "2prefix"), any of the values are acceptable. It also specified the `Mode` as `HeaderPrefix`, so any header that starts with those values is acceptable. - -Any of the following headers will match route2. -``` -Header2: 1prefix -``` -``` -Header2: 2prefix -``` -``` -Header2: 1prefix-extra -``` -``` -Header2: 2prefix-extra -``` -If a header contains multiple values, each one will be matched separately. The following request will match. -``` -Header2: foo, 1prefix, 2prefix -``` -The same holds if multiple values are split across multiple headers with the same name. -``` -Header2: 1prefix -Header2: 2prefix -``` -A single pair of enclosing quotes may be stripped from the value before matching. The following request will match. -``` -Header2: "2prefix" -``` -Multiple pairs of quotes will _not_ match. -``` -Header2: ""2prefix"" -``` - -### Scenario 3 - Exists - -Route3 only requires that the header "Header3" exists with any non-empty value - -The following is an example that will match route3. -``` -Header3: value -``` - -An empty header will _not_ match. -``` -Header3: -``` - -This mode does support headers with multiple values and multiple headers with the same name since it does not look at the header contents. The following will match. -``` -Header3: value1, value2 -``` -``` -Header3: value1 -Header3: value2 -``` -``` -Header3: -Header3: -``` - -### Scenario 4 - Multiple Headers - -Route4 requires both `header4` and `header5`, each matching according to their specified `Mode`. The following headers will match route4: -``` -Header4: value1 -Header5: AnyValue -``` -``` -Header4: value2 -Header5: AnyValue -``` - -These will _not_ match route4 because they are missing one of the required headers: -``` -Header4: value2 -``` -``` -Header5: AnyValue -``` - -### Scenario 5 - NotExists - -Route7 requires that the header "Header7" must not exists - -The following headers will match route7: - -``` -NotHeader7: AnyValue -``` - - -The following headers will _not_ match route7 because the header "Header7" exists. - -``` -Header7: AnyValue -``` -``` -Header7: -``` diff --git a/docs/docfx/articles/http-client-config.md b/docs/docfx/articles/http-client-config.md deleted file mode 100644 index ca99a1a82..000000000 --- a/docs/docfx/articles/http-client-config.md +++ /dev/null @@ -1,239 +0,0 @@ -# HTTP Client Configuration - -## Introduction - -Each [Cluster](xref:Yarp.ReverseProxy.Configuration.ClusterConfig) has a dedicated [HttpMessageInvoker](https://docs.microsoft.com/dotnet/api/system.net.http.httpmessageinvoker) instance used to forward requests to its [Destination](xref:Yarp.ReverseProxy.Configuration.DestinationConfig)s. The configuration is defined per cluster. On YARP startup, all clusters get new `HttpMessageInvoker` instances, however if later the cluster configuration gets changed the [IForwarderHttpClientFactory](xref:Yarp.ReverseProxy.Forwarder.IForwarderHttpClientFactory) will re-run and decide if it should create a new `HttpMessageInvoker` or keep using the existing one. The default `IForwarderHttpClientFactory` implementation creates a new `HttpMessageInvoker` when there are changes to the [HttpClientConfig](xref:Yarp.ReverseProxy.Configuration.HttpClientConfig). - -Properties of outgoing requests for a given cluster can be configured as well. They are defined in [ForwarderRequestConfig](xref:Yarp.ReverseProxy.Forwarder.ForwarderRequestConfig). - -The configuration is represented differently if you're using the [IConfiguration](https://docs.microsoft.com/dotnet/api/microsoft.extensions.configuration.iconfiguration) model or the code-first model. - -## IConfiguration -These types are focused on defining serializable configuration. The code based configuration model is described below in the "Code Configuration" section. - -### HttpClient -HTTP client configuration is based on [HttpClientConfig](xref:Yarp.ReverseProxy.Configuration.HttpClientConfig) and represented by the following configuration schema. If you need a more granular approach, please use a [custom implementation](https://microsoft.github.io/reverse-proxy/articles/http-client-config.html#custom-iforwarderhttpclientfactory) of `IForwarderHttpClientFactory`. -```JSON -"HttpClient": { - "SslProtocols": [ "" ], - "MaxConnectionsPerServer": "", - "DangerousAcceptAnyServerCertificate": "", - "RequestHeaderEncoding": "", - "ResponseHeaderEncoding": "", - "EnableMultipleHttp2Connections": "", - "WebProxy": { - "Address": "", - "BypassOnLocal": "", - "UseDefaultCredentials": "" - } -} -``` - -Configuration settings: -- SslProtocols - [SSL protocols](https://docs.microsoft.com/dotnet/api/system.security.authentication.sslprotocols) enabled on the given HTTP client. Protocol names are specified as array of strings. Default value is [None](https://docs.microsoft.com/dotnet/api/system.security.authentication.sslprotocols#System_Security_Authentication_SslProtocols_None). -```JSON -"SslProtocols": [ - "Tls11", - "Tls12" -] -``` -- MaxConnectionsPerServer - maximal number of HTTP 1.1 connections open concurrently to the same server. Default value is [int32.MaxValue](https://docs.microsoft.com/dotnet/api/system.int32.maxvalue). -```JSON -"MaxConnectionsPerServer": "10" -``` -- DangerousAcceptAnyServerCertificate - indicates whether the server's SSL certificate validity is checked by the client. Setting it to `true` completely disables validation. Default value is `false`. -```JSON -"DangerousAcceptAnyServerCertificate": "true" -``` -- RequestHeaderEncoding - enables other than ASCII encoding for outgoing request headers. Setting this value will leverage [`SocketsHttpHandler.RequestHeaderEncodingSelector`](https://docs.microsoft.com/dotnet/api/system.net.http.socketshttphandler.requestheaderencodingselector) and use the selected encoding for all headers. The value is then parsed by [`Encoding.GetEncoding`](https://docs.microsoft.com/dotnet/api/system.text.encoding.getencoding#System_Text_Encoding_GetEncoding_System_String_), use values like: "utf-8", "iso-8859-1", etc. -```JSON -"RequestHeaderEncoding": "utf-8" -``` -- ResponseHeaderEncoding - enables other than ASCII encoding for incoming response headers (from requests that the proxy would forward out). Setting this value will leverage [`SocketsHttpHandler.ResponseHeaderEncodingSelector`](https://docs.microsoft.com/dotnet/api/system.net.http.socketshttphandler.responseheaderencodingselector) and use the selected encoding for all headers. The value is then parsed by [`Encoding.GetEncoding`](https://docs.microsoft.com/dotnet/api/system.text.encoding.getencoding#System_Text_Encoding_GetEncoding_System_String_), use values like: "utf-8", "iso-8859-1", etc. -```JSON -"ResponseHeaderEncoding": "utf-8" -``` -Note that if you're using an encoding other than ASCII, you also need to set your server to accept requests and/or send responses with such headers. For example, when using Kestrel as the server, use [`KestrelServerOptions.RequestHeaderEncodingSelector`](https://docs.microsoft.com/dotnet/api/Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.RequestHeaderEncodingSelector) / [`.ResponseHeaderEncodingSelector`](https://docs.microsoft.com/dotnet/api/Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.ResponseHeaderEncodingSelector) to configure Kestrel to allow `Latin1` ("`iso-8859-1`") headers: -```C# -var builder = WebApplication.CreateBuilder(args); -builder.WebHost.ConfigureKestrel(kestrel => -{ - kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1; - // and/or - kestrel.ResponseHeaderEncodingSelector = _ => Encoding.Latin1; -}); -``` -- EnableMultipleHttp2Connections - enables opening additional HTTP/2 connections to the same server when the maximum number of concurrent streams is reached on all existing connections. The default is `true`. See [SocketsHttpHandler.EnableMultipleHttp2Connections](https://docs.microsoft.com/dotnet/api/system.net.http.socketshttphandler.enablemultiplehttp2connections) -```JSON -"EnableMultipleHttp2Connections": false -``` -- WebProxy - Enables sending requests through an outbound HTTP proxy to reach the destinations. See [`SocketsHttpHandler.Proxy`](https://docs.microsoft.com/dotnet/api/system.net.http.socketshttphandler.proxy) for details. - - Address - The url address of the outbound proxy. - - BypassOnLocal - A bool indicating if requests to local addresses should bypass the outbound proxy. - - UseDefaultCredentials - A bool indicating if the current application credentials should be use to authenticate to the outbound proxy. ASP.NET Core does not impersonate authenticated users for outbound requests. -```JSON -"WebProxy": { - "Address": "http://myproxy:8080", - "BypassOnLocal": "true", - "UseDefaultCredentials": "false" -} -``` - -### HttpRequest -HTTP request configuration is based on [ForwarderRequestConfig](xref:Yarp.ReverseProxy.Forwarder.ForwarderRequestConfig) and represented by the following configuration schema. -```JSON -"HttpRequest": { - "ActivityTimeout": "", - "Version": "", - "VersionPolicy": ["RequestVersionOrLower", "RequestVersionOrHigher", "RequestVersionExact"], - "AllowResponseBuffering": "" -} -``` - -Configuration settings: -- ActivityTimeout - how long a request is allowed to remain idle between any operation completing, after which it will be canceled. The default is 100 seconds. The timeout will reset when response headers are received or after successfully reading or writing any request, response, or streaming data like gRPC or WebSockets. TCP keep-alives and HTTP/2 protocol pings will not reset the timeout, but WebSocket pings will. -- Version - outgoing request [version](https://docs.microsoft.com/dotnet/api/system.net.http.httprequestmessage.version). The supported values at the moment are `1.0`, `1.1`, `2` and `3`. Default value is 2. -- VersionPolicy - defines how the final version is selected for the outgoing requests. See [HttpRequestMessage.VersionPolicy](https://docs.microsoft.com/dotnet/api/system.net.http.httprequestmessage.versionpolicy). The default value is `RequestVersionOrLower`. -- AllowResponseBuffering - allows to use write buffering when sending a response back to the client, if the server hosting YARP (e.g. IIS) supports it. **NOTE**: enabling it can break SSE (server side event) scenarios. - - -## Configuration example -The below example shows 2 samples of HTTP client and request configurations for `cluster1` and `cluster2`. - -```JSON -{ - "Clusters": { - "cluster1": { - "LoadBalancingPolicy": "Random", - "HttpClient": { - "SslProtocols": [ - "Tls11", - "Tls12" - ], - "MaxConnectionsPerServer": "10", - "DangerousAcceptAnyServerCertificate": "true" - }, - "HttpRequest": { - "ActivityTimeout": "00:00:30" - }, - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10000/" - }, - "cluster1/destination2": { - "Address": "http://localhost:10010/" - } - } - }, - "cluster2": { - "HttpClient": { - "SslProtocols": [ - "Tls12" - ] - }, - "HttpRequest": { - "Version": "1.1", - "VersionPolicy": "RequestVersionExact" - }, - "Destinations": { - "cluster2/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } -} -``` - -## Code Configuration -HTTP client configuration uses the type [HttpClientConfig](xref:Yarp.ReverseProxy.Configuration.HttpClientConfig). - -The following is an example of `HttpClientConfig` using [code based](config-providers.md) configuration. An instance of `HttpClientConfig` is assigned to the [ClusterConfig.HttpClient](xref:Yarp.ReverseProxy.Configuration.ClusterConfig) property before passing the cluster array to `LoadFromMemory` method. - -```C# -var routes = new[] -{ - new RouteConfig() - { - RouteId = "route1", - ClusterId = "cluster1", - Match = - { - Path = "{**catch-all}" - } - } -}; -var clusters = new[] -{ - new ClusterConfig() - { - ClusterId = "cluster1", - Destinations = - { - { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } } - }, - HttpClient = new HttpClientConfig { MaxConnectionsPerServer = 10, SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 } - } -}; - -services.AddReverseProxy() - .LoadFromMemory(routes, clusters); -``` - -## Configuring the http client - -`ConfigureHttpClient` provides a callback to customize the `SocketsHttpHandler` settings used for proxying requests. This will be called each time a cluster is added or changed. Cluster settings are applied to the handler before the callback. Custom data can be provided in the cluster metadata. This example shows adding a client certificate that will authenticate the proxy to the destination servers. - -```C# -var clientCert = new X509Certificate2("path"); -services.AddReverseProxy() - .ConfigureHttpClient((context, handler) => - { - handler.SslOptions.ClientCertificates.Add(clientCert); - }) -``` - -## Custom IForwarderHttpClientFactory -If direct control of HTTP client construction is necessary, the default [IForwarderHttpClientFactory](xref:Yarp.ReverseProxy.Forwarder.IForwarderHttpClientFactory) can be replaced with a custom one. For some customizations you can derive from the default [ForwarderHttpClientFactory](xref:Yarp.ReverseProxy.Forwarder.ForwarderHttpClientFactory) and override the methods that configure the client. - -It's recommended that any custom factory set the following `SocketsHttpHandler` properties to the same values as the default factory does in order to preserve a correct reverse proxy behavior and avoid unnecessary overhead. - -```C# -new SocketsHttpHandler -{ - UseProxy = false, - AllowAutoRedirect = false, - AutomaticDecompression = DecompressionMethods.None, - UseCookies = false, - EnableMultipleHttp2Connections = true, - ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), - ConnectTimeout = TimeSpan.FromSeconds(15), -}; -``` - -Always return an HttpMessageInvoker instance rather than an HttpClient instance which derives from HttpMessageInvoker. HttpClient buffers responses by default which breaks streaming scenarios and increases memory usage and latency. - -Custom data can be provided in the cluster metadata. - -The below is an example of a custom `IForwarderHttpClientFactory` implementation. - -```C# -public class CustomForwarderHttpClientFactory : IForwarderHttpClientFactory -{ - public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) - { - var handler = new SocketsHttpHandler - { - UseProxy = false, - AllowAutoRedirect = false, - AutomaticDecompression = DecompressionMethods.None, - UseCookies = false, - EnableMultipleHttp2Connections = true, - ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), - ConnectTimeout = TimeSpan.FromSeconds(15), - }; - - return new HttpMessageInvoker(handler, disposeHandler: true); - } -} -``` diff --git a/docs/docfx/articles/http3.md b/docs/docfx/articles/http3.md deleted file mode 100644 index 3472a5bb1..000000000 --- a/docs/docfx/articles/http3.md +++ /dev/null @@ -1,27 +0,0 @@ -# HTTP/3 - -## Introduction -YARP 1.1 supports HTTP/3 for inbound and outbound connections using the HTTP/3 support in .NET 7. To enable the HTTP/3 protocol in YARP you need to: -- Configure inbound connections in Kestrel -- Configure outbound connections in HttpClient - -## Set up HTTP/3 on Kestrel - -Protocols are required in the listener options: -```C# -var builder = WebApplication.CreateBuilder(args); -builder.WebHost.ConfigureKestrel(kestrel => -{ - kestrel.ListenAnyIP(443, portOptions => - { - portOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3; - portOptions.UseHttps(); - }); -}); -``` - -## HttpClient - -The default version of HttpRequest should be replaced by "3", find more details about [HttpRequest configuration](http-client-config.md#httprequest). - - diff --git a/docs/docfx/articles/https-tls.md b/docs/docfx/articles/https-tls.md deleted file mode 100644 index de3a61e4a..000000000 --- a/docs/docfx/articles/https-tls.md +++ /dev/null @@ -1,29 +0,0 @@ -# HTTPS & TLS - -HTTPS (HTTP over TLS encrypted connections) is the standard way to make HTTP requests on the Internet for security, integrity, and privacy reasons. There are several HTTPS/TLS considerations to account for when using a reverse proxy like YARP. - -## TLS Termination - -YARP is a level 7 HTTP proxy which means that incoming HTTPS/TLS connections are fully decrypted by the proxy so it can process and forward the HTTP requests. This is commonly known as TLS Termination. The outgoing connections to the destination(s) may or may not be encrypted, depending on the configuration provided. - -## TLS tunneling (CONNECT) - -TLS tunneling using the CONNECT method is a feature used to proxy requests without decrypting them. This is _not_ supported by YARP and there are no plans to add it. - -## Configuring incoming connections - -YARP can run on top of all ASP.NET Core servers and configuring HTTPS/TLS for incoming connections is server specific. Check the docs for [Kestrel](https://docs.microsoft.com/aspnet/core/fundamentals/servers/kestrel/endpoints#listenoptionsusehttps), [IIS](https://docs.microsoft.com/iis/manage/configuring-security/how-to-set-up-ssl-on-iis), and [Http.Sys](https://docs.microsoft.com/aspnet/core/fundamentals/servers/httpsys#configure-windows-server-1) for configuration details. - -### Advanced TLS filters with Kestrel - -Kestrel supports intercepting incoming connections before the TLS handshake. YARP includes a [TlsFrameHelper](xref:Yarp.ReverseProxy.Utilities.Tls.TlsFrameHelper) API that can parse the raw TLS handshake and enable you to gather custom telemetry or eagerly reject connections. These APIs cannot modify the TLS handshake or decrypt the data stream. See this [example](https://github.com/microsoft/reverse-proxy/blob/v1.0.0-rc.1/testassets/ReverseProxy.Direct/TlsFilter.cs). - -## Configuring outgoing connections - -To enable TLS encryption when communicating with a destination specify the destination address as `https` like `"https://destinationHost"`. See the [configuration docs](config-files.md#configuration-structure) for examples. - -The host name specified in the destination address will be used for the TLS handshake by default, including SNI and server certificate validation. If proxying the [original host header](transforms.md#requestheaderoriginalhost) is enabled, that value will be used for the TLS handshake instead. If a custom host value needs to be used then use the [RequestHeader](transforms.md#requestheader) transform to set the host header. - -Outbound connections to the destinations are handled by HttpClient/SocketsHttpHandler. A different instance and settings can be configured per cluster. Some settings are available in the configuration model, while others can only be configured in code. See the [HttpClient](http-client-config.md) docs for details. - -Destination server certificates need to be trusted by the proxy or custom validation needs to be applied via the [HttpClient](http-client-config.md) configuration. diff --git a/docs/docfx/articles/httpsys-delegation.md b/docs/docfx/articles/httpsys-delegation.md deleted file mode 100644 index 02cb973a9..000000000 --- a/docs/docfx/articles/httpsys-delegation.md +++ /dev/null @@ -1,71 +0,0 @@ -# Http.sys Delegation - -## Introduction -Http.sys delegation is a kernel level feature added into newer versions of Windows which allows a request to be transferred from the receiving process's http.sys queue to a target process's http.sys queue with very little overhead or added latency. For this delegation to work, the receiving process is only allowed to read the request headers. If the body has started to be read or a response has started, trying to delegate the request will fail. The response will not be visible to the proxy after delegation, which limits the functionality of the session affinity and passive health checks components, as well as some of the load balancing algorithms. Internally, YARP leverage's ASP.NET Core's [IHttpSysRequestDelegationFeature](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.server.httpsys.ihttpsysrequestdelegationfeature) - -## Requirements -Http.sys delegation requires: -- [ASP.NET Core's Http.sys server](https://docs.microsoft.com/aspnet/core/fundamentals/servers/httpsys) -- Windows Server 2019 or Windows 10 (build number 1809) or newer. - -## Defaults -Http.sys delegation won't be used unless added to the proxy pipeline and enabled in the destination configuration. - -## Configuration -Http.sys delegation can be enabled per destination by adding the `HttpSysDelegationQueue` metadata to the destination. The value of this metadata should be the target http.sys queue name. The destination's Address is used to specify the url prefix of the http.sys queue. - -```json -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}" - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "http://*:80/", - "Metadata": { - "HttpSysDelegationQueue": "TargetHttpSysQueueName" - } - } - } - } - } - } -} -``` - -In host configuration, configure the host to use the Http.sys server. -```c# -webBuilder.UseHttpSys(); -``` - -In application configuration, use the `MapReverseProxy` overload that lets you customize the pipeline and add http.sys delegation by calling `UseHttpSysDelegation`. -```c# -app.MapReverseProxy(proxyPipeline => -{ - // Add the three middleware YARP adds by default plus the Http.sys delegation middleware - proxyPipeline.UseSessionAffinity(); // Has no affect on delegation destinations - proxyPipeline.UseLoadBalancing(); - proxyPipeline.UsePassiveHealthChecks(); - proxyPipeline.UseHttpSysDelegation(); -}); -``` - -## Delegation Queue Lifetime -When YARP is configured to use delegation for a destination, a handle is created to the specified http.sys queue. This handle is kept alive as long as the destinations referencing it exist. Clean up of these handles is done during GC, so it's possible cleanup of the handle is delayed if it ends up in Gen2. This may cause issues for some receivers during process restart because if they try to create the queue during startup it will fail (because it still exists since YARP has a handle to it). The receivers need to be smart enough to attach instead and propertly re-setup the queue. ASP.NET Core's http.sys server [has this issue](https://github.com/dotnet/aspnetcore/issues/40359). - -YARP exposes a way to reset its handle to the queue. This allows consumers to write custom logic to determin if/when the handle to the queue should be cleaned up. - -Example: - -```c# -var delegator = app.Services.GetRequiredService(); -delegator.ResetQueue("TargetHttpSysQueueName", "http://*:80"); -``` diff --git a/docs/docfx/articles/index.md b/docs/docfx/articles/index.md deleted file mode 100644 index 458dc47a0..000000000 --- a/docs/docfx/articles/index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -uid: intro -title: Welcome to the YARP documentation! ---- - -# Welcome to the docs - -This is the documentation for YARP 2.0. - -For documentation of YARP 1.1.1, see https://github.com/microsoft/reverse-proxy/tree/release/1.1/docs/docfx/articles. \ No newline at end of file diff --git a/docs/docfx/articles/kubernetes-ingress.md b/docs/docfx/articles/kubernetes-ingress.md deleted file mode 100644 index 47fc4daa9..000000000 --- a/docs/docfx/articles/kubernetes-ingress.md +++ /dev/null @@ -1,109 +0,0 @@ -# Kubernetes Ingress Controller - -Introduced: Future Preview - -YARP can be integrated with Kubernetes as a reverse proxy managing HTTP/HTTPS traffic ingress to a Kubernetes cluster. Currently, the module is shipped as a separate package and is in preview. - -## Prerequisites - -Before we continue with this tutorial, make sure you have the following ready... - -1. Installing [docker](https://docs.docker.com/install/) based on your operating system. - -2. A container registry. Docker by default will create a container registry on [DockerHub](https://hub.docker.com/). You could also use [Azure Container Registry](https://docs.microsoft.com/en-us/azure/aks/tutorial-kubernetes-prepare-acr) or another container registry of your choice, like a [local registry](https://docs.docker.com/registry/deploying/#run-a-local-registry) for testing. - -3. A Kubernetes Cluster. There are many different options here, including: - - [Azure Kubernetes Service](https://docs.microsoft.com/en-us/azure/aks/tutorial-kubernetes-deploy-cluster) - - [Kubernetes in Docker Desktop](https://www.docker.com/blog/docker-windows-desktop-now-kubernetes/), however it does take up quite a bit of memory on your machine, so use with caution. - - [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) - - [K3s](https://k3s.io), a lightweight single-binary certified Kubernetes distribution from Rancher. - - Another Kubernetes provider of your choice. - -> :warning: If you choose a container registry provided by a cloud provider (other than Dockerhub), you will likely have to take some steps to configure your kubernetes cluster to allow access. Follow the instructions provided by your cloud provider. - -## Get Started - -> :warning: For now there is no official docker image for the YARP ingress controller. - -So, in the meantime we need to build the YARP ingress controller locally and deploy it. In the root of the repository, run: - -``` -docker build . -t /yarp-controller: -f .\src\Kubernetes.Controller\Dockerfile -docker push /yarp-controller: -``` - -where `REGISTRY_NAME` is the name of your docker registry and `TAG` is a tag for the image (for example 1.0.0). - -Then the first step will be to deploy the YARP ingress controller to the Kubernetes cluster. This can be done by navigating to Kubernetes Ingress sample `\samples\KuberenetesIngress.Sample\Ingress` -and running (after modifying `ingress-controller.yaml` with the same `REGISTRY_NAME` and `TAG`): - -``` -kubectl apply -f ingress-controller.yaml -``` - -To verify that the ingress controller has been deployed, run: - -``` -kubectl get pods -n yarp -``` - -You can then check logs from the ingress controller by running: - -``` -kubectl logs -n yarp -``` - -> :bulb: All services, deployments, and pods for YARP are in the namespace `yarp`. Make sure to include `-n yarp` if you want to check on the status of yarp. - -Next, we need to build and deploy our ingress. In the root of the repository, run: - -``` -docker build . -t /yarp: -f .\samples\KuberenetesIngress.Sample\Ingress\Dockerfile -docker push /yarp: -``` - -where `REGISTRY_NAME` is the name of your docker registry and `TAG` is a tag for the image (for example 1.0.0). - -Finally, we need to deploy the ingress itself to Kubernetes. To do this navigate again to the `Ingress` directory, modify the `ingress.yaml` file for your registry and tag specified earlier and run: - -``` -kubectl apply -f .\ingress.yaml -``` - -At this point, your ingress and controller should be running. - -## Deploying an app - -To use the ingress, we now need to deploy an application to Kubernetes. Navigate to `samples\KuberenetesIngress.Sample\backend` and run: - -``` -docker build . -t /backend: -docker push /backend: -``` - -And deploying it to kubernetes by running (after modifying `backend.yaml` with the same `REGISTRY_NAME` and `TAG`): - -``` -kubectl apply -f .\backend.yaml -``` - -## Creating the ingress definition - -Finally, once we have deployed the backend application, we need to route traffic to the backend. To do this, run in the `backend` directory: - -``` -kubectl apply -f .\ingress-sample.yaml -``` - -And then run: - -``` - kubectl get service -n yarp -``` - -to get the external IP of the ingress, the name of the related service being `yarp-proxy`. - -> :bulb: If you are using a local K8s cluster and don't get an external IP, you may need to not use the default `port: 80` for the `yarp-proxy` service. -If so, try to update again the `ingress.yaml` file to use another port e.g. `port: 8085` and then re-deploy the ingress to Kubernetes. - -Navigate to the external IP, and you should see the backend information. diff --git a/docs/docfx/articles/lets-encrypt.md b/docs/docfx/articles/lets-encrypt.md deleted file mode 100644 index 4a382a3c9..000000000 --- a/docs/docfx/articles/lets-encrypt.md +++ /dev/null @@ -1,68 +0,0 @@ -# Lets Encrypt - -## Introduction -YARP can support the certificate authority [Lets Encrypt](https://letsencrypt.org/) by using the API of another ASP.NET Core project [LettuceEncrypt](https://github.com/natemcmaster/LettuceEncrypt). It allows you to set up TLS between the client and YARP with minimal configuration. - -## Requirements - -Add the LettuceEncrypt package dependency: -```csproj - -``` - -## Configuration -There are required options for LettuceEncrypt that should be set, see the example of `appsettings.json`: - -```JSON -{ - "Urls": "http://*:80;https://*:443", - - "Logging": { ... }, - - "ReverseProxy": { - "Routes": { ... }, - "Clusters": { ... } - }, - - "LettuceEncrypt": { - // Set this to automatically accept the terms of service of your certificate authority. - // If you don't set this in config, you will need to press "y" whenever the application starts - "AcceptTermsOfService": true, - - // You must specify at least one domain name - "DomainNames": [ "example.com" ], - - // You must specify an email address to register with the certificate authority - "EmailAddress": "it-admin@example.com" - } -} -``` - -## Update Services - -```C# -services.AddLettuceEncrypt(); -``` - -For more options (i.e. saving certificates) see examples in [LettuceEncrypt doc](https://github.com/natemcmaster/LettuceEncrypt). - -## Kestrel Endpoints - -If your project is explicitly using kestrel options to configure IP addresses, ports, or HTTPS settings, you will also need to call `UseLettuceEncrypt`. - -Example: - -```C# -var builder = WebApplication.CreateBuilder(args); -builder.WebHost.ConfigureKestrel(kestrel => -{ - kestrel.ListenAnyIP(443, portOptions => - { - portOptions.UseHttps(h => - { - h.UseLettuceEncrypt(kestrel.ApplicationServices); - }); - }); -}); -``` - diff --git a/docs/docfx/articles/load-balancing.md b/docs/docfx/articles/load-balancing.md deleted file mode 100644 index 3765fb4fd..000000000 --- a/docs/docfx/articles/load-balancing.md +++ /dev/null @@ -1,103 +0,0 @@ -# Load Balancing - -## Introduction - -Whenever there are multiple healthy destinations available, YARP has to decide which one to use for a given request. -YARP ships with built-in load-balancing algorithms, but also offers extensibility for any custom load balancing approach. - -## Configuration - -### Services and middleware registration - -Load balancing policies are registered in the DI container via the `AddLoadBalancingPolicies()` method, which is automatically called by `AddReverseProxy()`. - -The middleware is added with `UseLoadBalancing()`, which is included by default in the parameterless `MapReverseProxy` method. - -### Cluster configuration - -The algorithm used to determine the destination can be configured by setting the `LoadBalancingPolicy`. - -If no policy is specified, `PowerOfTwoChoices` will be used. - -#### File example - -```JSON -"ReverseProxy": { - "Clusters": { - "cluster1": { - "LoadBalancingPolicy": "RoundRobin", - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10000/" - }, - "cluster1/destination2": { - "Address": "https://localhost:10010/" - } - } - } - } -} -``` - -#### Code example - -```C# -var clusters = new[] -{ - new ClusterConfig() - { - ClusterId = "cluster1", - LoadBalancingPolicy = LoadBalancingPolicies.RoundRobin, - Destinations = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }, - { "destination2", new DestinationConfig() { Address = "https://localhost:10010" } } - } - } -}; -``` - -## Built-in policies - -YARP ships with the following built-in policies: -- `FirstAlphabetical` - - Select the alphabetically first available destination without considering load. This is useful for dual destination fail-over systems. -- `Random` - - Select a destination randomly. -- `PowerOfTwoChoices` (default) - - Select two random destinations and then select the one with the least assigned requests. - This avoids the overhead of `LeastRequests` and the worst case for `Random` where it selects a busy destination. -- `RoundRobin` - - Select a destination by cycling through them in order. -- `LeastRequests` - - Select the destination with the least assigned requests. This requires examining all destinations. - -## Extensibility - -`ILoadBalancingPolicy` is responsible for picking a destination from a list of available healthy destinations. - -A custom implementation can be provided in DI. - -```c# -// Implement the ILoadBalancingPolicy -public sealed class LastLoadBalancingPolicy : ILoadBalancingPolicy -{ - public string Name => "Last"; - - public DestinationState? PickDestination(HttpContext context, ClusterState cluster, IReadOnlyList availableDestinations) - { - return availableDestinations[^1]; - } -} - -// Register it in DI in ConfigureServices method -services.AddSingleton(); - -// Set the LoadBalancingPolicy on the cluster -cluster.LoadBalancingPolicy = "Last"; -``` diff --git a/docs/docfx/articles/middleware.md b/docs/docfx/articles/middleware.md deleted file mode 100644 index acc1e2cb8..000000000 --- a/docs/docfx/articles/middleware.md +++ /dev/null @@ -1,148 +0,0 @@ -# Middleware - -## Introduction - -ASP.NET Core uses a [middleware pipeline](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/) to divide request processing into discrete steps. The app developer can add and order middleware as needed. ASP.NET Core middleware is also used to implement and customize reverse proxy functionality. - -## Defaults -The [getting started](getting-started.md) sample shows the following Configure method. This sets up a middleware pipeline with development tools, routing, and proxy configured endpoints (`MapReverseProxy`). - -```C# -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddReverseProxy() - .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); -var app = builder.Build(); -app.MapReverseProxy(); -app.Run(); -``` - -The parmeterless `MapReverseProxy()` in [ReverseProxyIEndpointRouteBuilderExtensions](xref:Microsoft.AspNetCore.Builder.ReverseProxyIEndpointRouteBuilderExtensions) overload includes all standard proxy middleware for [session affinity](session-affinity.md), [load balancing](load-balancing.md), [passive health checks](dests-health-checks.md#passive-health-checks), and the final proxying of the request. Each of these check the configuration of the matched route, cluster, and destination and perform their task accordingly. - -## Adding Middleware - -Middleware added to your application pipeline will see the request in different states of processing depending on where the middleware is added. Middleware added before `UseRouting` will see all requests and can manipulate them before any routing takes place. Middleware added between `UseRouting` and `UseEndpoints` can call `HttpContext.GetEndpoint()` to check which endpoint routing matched the request to (if any), and use any metadata that was associated with that endpoint. This is how [Authentication, Authorization](authn-authz.md) and [CORS](cors.md) are handled. - -[ReverseProxyIEndpointRouteBuilderExtensions](xref:Microsoft.AspNetCore.Builder.ReverseProxyIEndpointRouteBuilderExtensions) provides an overload of `MapReverseProxy` that lets you build a middleware pipeline that will run only for requests matched to proxy configured routes. - -``` -app.MapReverseProxy(proxyPipeline => -{ - proxyPipeline.Use((context, next) => - { - // Custom inline middleware - - return next(); - }); - proxyPipeline.UseSessionAffinity(); - proxyPipeline.UseLoadBalancing(); - proxyPipeline.UsePassiveHealthChecks(); -}); -``` - -By default this overload of `MapReverseProxy` only includes the minimal setup, proxying logic, and limit enforcement at the start and end of its pipeline. Middleware for session affinity, load balancing, and passive health checks are not included by default so that you can exclude, replace, or control their ordering with any additional middleware. - -## Custom Proxy Middleware - -Middleware inside the `MapReverseProxy` pipeline have access to all of the proxy data and state associated with a request (the route, cluster, destinations, etc.) through the [IReverseProxyFeature](xref:Yarp.ReverseProxy.Model.IReverseProxyFeature). This is available from `HttpContext.Features` or the extension method `HttpContext.GetReverseProxyFeature()`. - -The data in `IReverseProxyFeature` are snapshotted from the proxy configuration at the start of the proxy pipeline and will not be affected by proxy configuration changes that occur while the request is being processed. - -```C# -proxyPipeline.Use((context, next) => -{ - var proxyFeature = context.GetReverseProxyFeature(); - var cluster = proxyFeature.Cluster; - var destinations = proxyFeature.AvailableDestinations; - - return next(); -}); -``` - -## What to do with middleware - -Middleware can generate logs, control if a request gets proxied or not, influence where it's proxied to, and add additional features like error handling, retries, etc.. - -### Logs and Metrics - -Middleware can inspect request and response fields to generate logs and aggregate metrics. See the note about bodies under "What not to do with middleware" below. - -```C# -proxyPipeline.Use(async (context, next) => -{ - LogRequest(context); - await next(); - LogResponse(context); -}); -``` - -### Send an immediate response - -If a middleware inspects a request and determines that it should not be proxied, it may generate its own response and return control to the server without calling `next()`. - -```C# -proxyPipeline.Use((context, next) => -{ - if (!CheckAllowedRequest(context, out var reason)) - { - context.Response.StatusCode = StatusCodes.Status400BadRequest; - return context.Response.WriteAsync(reason); - } - - return next(); -}); -``` - -### Filter destinations - -Middleware like session affinity and load balancing examine the `IReverseProxyFeature` and the cluster configuration to decide which destination a request should be sent to. - -`AllDestinations` lists all destinations in the selected cluster. - -`AvailableDestinations` lists the destinations currently considered eligible to handle the request. It is initialized to `AllDestinations`, excluding unhealthy ones if health checks are enabled. `AvailableDestinations` should be reduced to a single destination by the end of the pipeline or else one will be selected randomly from the remainder. - -`ProxiedDestination` is set by the proxy logic at the end of the pipeline to indicate which destination was ultimately used. If there are no available destinations remaining then a 503 error response is sent. - -```C# -proxyPipeline.Use(async (context, next) => -{ - var proxyFeature = context.GetReverseProxyFeature(); - proxyFeature.AvailableDestinations = Filter(proxyFeature.AvailableDestinations); - - await next(); - - Report(proxyFeature.ProxiedDestination); -}); -``` - -`DestinationState` implements `IReadOnlyList` so a single destination can be assigned to `AvailableDestinations` without creating a new list. - -### Error handling - -Middleware can wrap the call to `await next()` in a try/catch block to handle exceptions from later components. - -The proxy logic at the end of the pipeline ([IHttpForwarder](direct-forwarding.md)) does not throw exceptions for common request proxy errors. These are captured and reported in the [IForwarderErrorFeature](xref:Yarp.ReverseProxy.Forwarder.IForwarderErrorFeature) available from `HttpContext.Features` or the `HttpContext.GetForwarderErrorFeature()` extension method. - -```C# -proxyPipeline.Use(async (context, next) => -{ - await next(); - - var errorFeature = context.GetForwarderErrorFeature(); - if (errorFeature is not null) - { - Report(errorFeature.Error, errorFeature.Exception); - } -}); -``` - -If the response has not started (`HttpResponse.HasStarted`) it can be cleared (`HttpResponse.Clear()`) and an alternate response sent, or the proxy feature fields may be reset and the request retried. - -## What not to do with middleware - -Middleware should be cautious about modifying request fields such as headers in order to affect the outgoing proxied request. Such modifications may interfere with features like retries and may be better handled by [transforms](transforms.md). - -Middleware MUST check `HttpResponse.HasStarted` before modifying response fields after calling `next()`. If the response has already started being sent to the client then the middleware can no longer modify it (except maybe Trailers). [Transforms](transforms.md) can be used to inspect and suppress unwanted responses. Otherwise see the next note. - -Middleware should avoid interacting with the request or response bodies. Bodies are not buffered by default, so interacting with them can prevent them from reaching their destinations. While enabling buffering is possible, it's discouraged as it can add significant memory and latency overhead. Using a wrapped, streaming approach is recommended if the body must be examined or modified. See the [ResponseCompression](https://github.com/dotnet/aspnetcore/blob/24588220006bc164b63293129cc94ac6292250e4/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs#L55-L73) middleware for an example. - -Middleware MUST NOT do any multi-threaded work on an individual request, `HttpContext` and its associated members are not thread safe. diff --git a/docs/docfx/articles/output-caching.md b/docs/docfx/articles/output-caching.md deleted file mode 100644 index 36c460e8c..000000000 --- a/docs/docfx/articles/output-caching.md +++ /dev/null @@ -1,63 +0,0 @@ -# Output Caching - -## Introduction -The reverse proxy can be used to cache proxied responses and serve requests before they are proxied to the destination servers. This can reduce load on the destination servers, add a layer of protection, and ensure consistent policies are implemented across your applications. - -> This feature is only available when using .NET 7.0 or later - -## Defaults - -No output caching is performed unless enabled in the route or application configuration. - -## Configuration -Output Cache policies can be specified per route via [RouteConfig.OutputCachePolicy](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "OutputCachePolicy": "customPolicy", - "Match": { - "Hosts": [ "localhost" ] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } - } -} -``` - -[Output cache policies](https://learn.microsoft.com/aspnet/core/performance/caching/output) are an ASP.NET Core concept that the proxy utilizes. The proxy provides the above configuration to specify a policy per route and the rest is handled by existing ASP.NET Core output caching middleware. - -Output cache policies can be configured in Program.cs as follows: -```c# -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddOutputCache(options => -{ - options.AddPolicy("customPolicy", builder => builder.Expire(TimeSpan.FromSeconds(20))); -}); -``` - -Then add the output caching middleware: - -```c# -var app = builder.Build(); - -app.UseOutputCache(); - -app.MapReverseProxy(); -``` - -See the [Output Caching](https://learn.microsoft.com/aspnet/core/performance/caching/output) docs for setting up your preferred kind of output caching. diff --git a/docs/docfx/articles/packages-refs.md b/docs/docfx/articles/packages-refs.md deleted file mode 100644 index 60fdfec2c..000000000 --- a/docs/docfx/articles/packages-refs.md +++ /dev/null @@ -1,128 +0,0 @@ -# Packages referenced by YARP - -## [Global] -Packages installed to all projects. -| Package | License | -| :--- | :--- | -Microsoft.SourceLink.AzureRepos.Git | Apache-2.0 -Microsoft.SourceLink.GitHub | Apache-2.0 -XliffTasks* | MIT - -\* - installed to all projects excluding the ones ending with "*Tests" - -## docfx -| Package | License | -| :--- | :--- | -docfx.console | MIT -NETStandard.Library | MIT - -## Yarp.Kubernetes.Ingress -| Package | License | -| :--- | :--- | -Serilog.Extensions.Logging | Apache-2.0 -Serilog.Formatting.Compact | Apache-2.0 -Serilog.Sinks.Console | Apache-2.0 - -## ReverseProxy.Metrics.Prometheus.Sample -| Package | License | -| :--- | :--- | -prometheus-net | MIT -prometheus-net.AspNetCore | MIT - -## Microsoft.Kubernetes.Controller -| Package | License | -| :--- | :--- | -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) - -## Microsoft.Kubernetes.Core -| Package | License | -| :--- | :--- | -KubernetesClient | Apache-2.0 -Microsoft.AspNetCore.JsonPatch | Apache-2.0 -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) -Microsoft.Extensions.DependencyInjection.Abstractions | Apache-2.0 -Microsoft.Extensions.Options | Apache-2.0 - -## Microsoft.Kubernetes.CustomResources -| Package | License | -| :--- | :--- | -KubernetesClient | Apache-2.0 -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) -Microsoft.Extensions.DependencyInjection.Abstractions | Apache-2.0 -Microsoft.Extensions.Options | Apache-2.0 -NJsonSchema | MIT - -## Microsoft.Kubernetes.Operator -| Package | License | -| :--- | :--- | -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) - -## Microsoft.Kubernetes.ResourceKinds.OpenApi -| Package | License | -| :--- | :--- | -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) -NSwag.Core | MIT - -## Microsoft.Kubernetes.Testing -| Package | License | -| :--- | :--- | -KubernetesClient | Apache-2.0 -Microsoft.AspNetCore.Mvc.NewtonsoftJson | Apache-2.0 -Microsoft.CodeAnalysis.FxCopAnalyzers | [Microsoft .NET Library](https://dotnet.microsoft.com/en/dotnet_library_license.htm) - -## Yarp.Kubernetes.Controller -| Package | License | -| :--- | :--- | -Microsoft.VisualStudio.Azure.Containers.Tools.Targets | [Microsoft .Net Library](https://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm) -Serilog.Extensions.Logging | Apache-2.0 -Serilog.Formatting.Compact | Apache-2.0 -Serilog.Sinks.Console | Apache-2.0 - -## Yarp.ReverseProxy.FunctionalTests -| Package | License | -| :--- | :--- | -Microsoft.NET.Test.Sdk | [Microsoft .Net Library](https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm) -xunit | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.console | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.visualstudio | MIT - -## Yarp.Kubernetes.Tests -| Package | License | -| :--- | :--- | -Autofac | MIT -Autofac.Extras.Moq | MIT -coverlet.collector | MIT -Microsoft.NET.Test.Sdk | [Microsoft .Net Library](https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm) -xunit | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.console | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.visualstudio | MIT - -## Yarp.ReverseProxy.Tests -| Package | License | -| :--- | :--- | -Autofac | MIT -Autofac.Extras.Moq | MIT -coverlet.collector | MIT -Microsoft.AspNetCore.TestHost | Apache-2.0 -Microsoft.NET.Test.Sdk | [Microsoft .Net Library](https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm) -Moq | [BSD 3-Clause License](https://raw.githubusercontent.com/moq/moq4/master/License.txt) -xunit | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.console | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.visualstudio | MIT - -## Yarp.ReverseProxy.Tests.Common -| Package | License | -| :--- | :--- | -Autofac | MIT -Autofac.Extras.Moq | MIT -coverlet.collector | MIT -Microsoft.NET.Test.Sdk | [Microsoft .Net Library](https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm) -Moq | [BSD 3-Clause License](https://raw.githubusercontent.com/moq/moq4/master/License.txt) -xunit | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.console | [Multiple licenses](https://raw.githubusercontent.com/xunit/xunit/master/license.txt) -xunit.runner.visualstudio | MIT - -## BenchmarkApp -| Package | License | -| :--- | :--- | -Microsoft.Crank.EventSources | MIT diff --git a/docs/docfx/articles/queryparameter-routing.md b/docs/docfx/articles/queryparameter-routing.md deleted file mode 100644 index 320a68eda..000000000 --- a/docs/docfx/articles/queryparameter-routing.md +++ /dev/null @@ -1,363 +0,0 @@ ---- -uid: queryparameter-routing -title: Query Parameter Routing ---- - -# Query Parameter Based Routing - -Proxy routes specified in [config](config-files.md) or via [code](config-providers.md) must include at least a path or host to match against. In addition to these, a route can also specify one or more query parameters that must be present on the request. - -### Precedence - -The default route match precedence order is 1) path, 2) method, 3) host, 4) headers 5) query parameters. That means a route which specifies methods and no query parameters will match before a route which specifies query parameters and no methods. This can be overridden by setting the `Order` property on a route. - -## Configuration - -Query Parameters are specified in the `Match` section of a proxy route. - -If multiple query parameter rules are specified on a route then all must match for a route to be taken. OR logic must be implemented either within a query parameter rule or as separate routes. - -Configuration: -```JSON -"Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam1", - "Values": [ "value1" ], - "Mode": "Exact" - } - ] - } - }, - "route2" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam2", - "Values": [ "1prefix", "2prefix" ], - "Mode": "Prefix" - } - ] - } - }, - "route3" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam3", - "Mode": "Exists" - } - ] - } - }, - "route4" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam4", - "Values": [ "value1", "value2" ], - "Mode": "Exact" - }, - { - "Name": "queryparam5", - "Mode": "Exists" - } - ] - } - }, - "route5" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam5", - "Values": [ "value1", "value2" ], - "Mode": "Contains" - }, - { - "Name": "queryparam6", - "Mode": "Exists" - } - ] - } - }, - "route6" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam6", - "Values": [ "value1", "value2" ], - "Mode": "NotContains" - }, - { - "Name": "queryparam7", - "Mode": "Exists" - } - ] - } - } -} -``` - -Code: -```C# -var routes = new[] -{ - new RouteConfig() - { - RouteId = "route1", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam1", - Values = new[] { "value1" }, - Mode = QueryParameterMatchMode.Exact - } - } - } - }, - new RouteConfig() - { - RouteId = "route2", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam2", - Values = new[] { "1prefix", "2prefix" }, - Mode = QueryParameterMatchMode.Prefix - } - } - } - }, - new RouteConfig() - { - RouteId = "route3", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam3", - Mode = QueryParameterMatchMode.Exists - } - } - } - }, - new RouteConfig() - { - RouteId = "route4", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam4", - Values = new[] { "value1", "value2" }, - Mode = QueryParameterMatchMode.Exact - }, - new RouteQueryParameter() - { - Name = "QueryParam5", - Mode = QueryParameterMatchMode.Exists - } - } - } - }, - new RouteConfig() - { - RouteId = "route5", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam5", - Values = new[] { "value1", "value2" }, - Mode = QueryParameterMatchMode.Contains - } - } - } - }, - new RouteConfig() - { - RouteId = "route6", - ClusterId = "cluster1", - Match = new RouteMatch - { - Path = "{**catch-all}", - QueryParameters = new[] - { - new RouteQueryParameter() - { - Name = "QueryParam6", - Values = new[] { "value1", "value2" }, - Mode = QueryParameterMatchMode.NotContains - } - } - } - } -}; -``` - -## Contract - -[RouteQueryParameter](xref:Yarp.ReverseProxy.Configuration.RouteQueryParameter) defines the code contract and is mapped from config. - -### Name - -The query parameter name to check for on the request. A non-empty value is required. This field is case-insensitive. - -### Values - -A list of possible values to search for. The query parameter must match at least one of these values according to the specified `Mode` except for the 'NotContains'. At least one value is required unless `Mode` is set to `Exists`. - -### Mode - -[QueryParameterMatchMode](xref:Yarp.ReverseProxy.Configuration.QueryParameterMatchMode) specifies how to match the value(s) against the request query parameter. The default is `Exact`. -- Exact - The query parameter must match in its entirety, subject to the value of `IsCaseSensitive`. Only single query parameters are supported. If there are multiple query parameters with the same name then the match fails. -- Prefix - The query parameter must match by prefix, subject to the value of `IsCaseSensitive`. Only single query parameters are supported. If there are multiple query parameters with the same name then the match fails. -- Exists - The query parameter must exist and contain any non-empty value. -- Contains - The query parameter must contain the value for a match, subject to the value of `IsCaseSensitive`. Only single query parameters are supported. If there are multiple query parameters with the same name then the match fails. -- NotContains - The query parameter must not contain any of the match values, subject to the value of `IsCaseSensitive`. Only single query parameters are supported. If there are multiple query parameters with the same name then the match fails. - -### IsCaseSensitive - -Indicates if the value match should be performed as case sensitive or insensitive. The default is `false`, insensitive. - -### Encoding - -The request query string will be parsed and decoded before matching against the route rules. - -``` - "route8" : { - "ClusterId": "cluster1", - "Match": { - "Path": "{**catch-all}", - "QueryParameters": [ - { - "Name": "queryparam8", - "Values": [ "another value" ], - "Mode": "Exact" - } - ] - } -``` - -Matches to -``` -?queryparam8=another%20value -``` -or -``` -?queryparam8=another+value -``` - -## Examples - -These examples use the configuration specified above. - -### Scenario 1 - Exact Query Parameter Match - -A request with the following query parameter will match against route1. -``` -?QueryParam1=Value1 -``` - -Multiple query parameters with the same name are not currently supported and will _not_ match. -``` -?QueryParam1=Value1&QueryParam1=Value2 -``` - - -### Scenario 2 - Multiple Values - -Route2 defined multiple values to search for in a query parameter ("1prefix", "2prefix"), any of the values are acceptable. It also specified the `Mode` as `Prefix`, so any query parameter that starts with those values is acceptable. - -Any of the following query parameters will match route2. -``` -?QueryParam2=1prefix -``` -``` -?QueryParam2=2prefix -``` -``` -?QueryParam2=1prefix-extra -``` -``` -?QueryParam2=2prefix-extra -``` - -Multiple query parameters with the same name are not currently supported and will _not_ match. -``` -?QueryParam2=2prefix&QueryParam2=1prefix -``` - -### Scenario 3 - Exists - -Route3 only requires that the query parameter "QueryParam3" exists with any non-empty value - -The following is an example that will match route3. -``` -?QueryParam3=value -``` - -An empty query parameter will _not_ match. -``` -?QueryParam3 -?QueryParam3= -``` - -This mode does support query parameters with multiple values and multiple query parameters with the same name since it does not look at the query parameter contents. The following will match. -``` -?QueryParam3=value1&QueryParam3=value2 -``` - -### Scenario 4 - Multiple QueryParameters - -Route4 requires both `QueryParam4` and `QueryParam5`, each matching according to their specified `Mode`. The following query parameters will match route4: -``` -?QueryParam4=value1&QueryParam5=AnyValue -``` -``` -?QueryParam4=value2&QueryParam5=AnyValue -``` - -These will _not_ match route4 because they are missing one of the required query parameters: -``` -?QueryParam4=value2 -``` -``` -?QueryParam5=AnyValue -``` diff --git a/docs/docfx/articles/rate-limiting.md b/docs/docfx/articles/rate-limiting.md deleted file mode 100644 index 4c99ac32b..000000000 --- a/docs/docfx/articles/rate-limiting.md +++ /dev/null @@ -1,74 +0,0 @@ -# Rate Limiting - -## Introduction -The reverse proxy can be used to rate-limit requests before they are proxied to the destination servers. This can reduce load on the destination servers, add a layer of protection, and ensure consistent policies are implemented across your applications. - -> This feature is only available when using .NET 7.0 or later - -## Defaults - -No rate limiting is performed on requests unless enabled in the route or application configuration. However, the Rate Limiting middleware (`app.UseRateLimiter()`) can apply a default limiter applied to all routes, and this doesn't require any opt-in from the config. - -Example: -```c# -services.AddRateLimiter(options => options.GlobalLimiter = globalLimiter); -``` - -## Configuration -Rate Limiter policies can be specified per route via [RouteConfig.RateLimiterPolicy](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "RateLimiterPolicy": "customPolicy", - "Match": { - "Hosts": [ "localhost" ] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } - } -} -``` - -[RateLimiter policies](https://learn.microsoft.com/aspnet/core/performance/rate-limit) are an ASP.NET Core concept that the proxy utilizes. The proxy provides the above configuration to specify a policy per route and the rest is handled by existing ASP.NET Core rate limiting middleware. - -RateLimiter policies can be configured in services as follows: -```c# -services.AddRateLimiter(options => -{ - options.AddFixedWindowLimiter("customPolicy", opt => - { - opt.PermitLimit = 4; - opt.Window = TimeSpan.FromSeconds(12); - opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; - opt.QueueLimit = 2; - }); -}); -``` - -Then add the RateLimiter middleware. - -```c# -app.UseRateLimiter(); - -app.MapReverseProxy(); -``` - -See the [Rate Limiting](https://learn.microsoft.com/aspnet/core/performance/rate-limit) docs for setting up your preferred kind of rate limiting. - -### Disable Rate Limiting - -Specifying the value `disable` in a route's `RateLimiterPolicy` parameter means the rate limiter middleware will not apply any policies to this route, even the default policy. diff --git a/docs/docfx/articles/runtimes.md b/docs/docfx/articles/runtimes.md deleted file mode 100644 index 97eebf40a..000000000 --- a/docs/docfx/articles/runtimes.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -uid: runtimes -title: Supported Runtimes ---- - -# YARP Supported Runtimes - -YARP 2.0+ supports ASP.NET Core 6.0 and newer. You can download the .NET SDK from https://dotnet.microsoft.com/download/dotnet/. See [Releases](https://github.com/microsoft/reverse-proxy/releases) for specific version support. - -YARP is taking advantage of new .NET features and optimizations as they become available. This does mean that some features may not be available if you're running on the previous versions of ASP.NET. - -## Related 6.0 Runtime Improvements - -- [HTTP/3](http3.md) - support for inbound and outbound connections (preview). -- [Distributed Tracing](distributed-tracing.md) - .NET 6.0 has built-in configurable support that YARP takes advantage of to enable more scenarios out-of-the-box. -- [Http.sys Delegation](httpsys-delegation.md) - a kernel-level ASP.NET Core 6 feature that allows a request to be transferred to a different process. -- [UseHttpLogging](diagnosing-yarp-issues.md#using-aspnet-request-logging) - includes an additional middleware component that can be used to provide more details about the request and response. -- [Dynamic HTTP/2 window scaling](https://github.com/dotnet/runtime/pull/54755) - improves HTTP/2 download speed on high-latency connections. -- [NonValidated headers](https://github.com/microsoft/reverse-proxy/pull/1507) - improves perfomance by using non-validated HttpClient headers. - - -## Related 7.0 Runtime Improvements - -- [HTTP/3](http3.md) - support for inbound and outbound connections (stable). -- [Zero-byte reads on HttpClient's response streams](https://github.com/dotnet/runtime/pull/61913) - reduces memory usage. -- [Header allocation reductions](https://github.com/dotnet/runtime/pull/62981) - reduces memory usage. -- [Kestrel Http/2 perf improvements](https://github.com/dotnet/aspnetcore/pull/40925) - Improve contention and throughput for multiple requests on one connection. diff --git a/docs/docfx/articles/service-fabric-int.md b/docs/docfx/articles/service-fabric-int.md deleted file mode 100644 index 98119dd39..000000000 --- a/docs/docfx/articles/service-fabric-int.md +++ /dev/null @@ -1,3 +0,0 @@ -# Service Fabric Integration - -This functionality has been replaced by https://github.com/microsoft/service-fabric-yarp diff --git a/docs/docfx/articles/session-affinity.md b/docs/docfx/articles/session-affinity.md deleted file mode 100644 index 85bf70bd8..000000000 --- a/docs/docfx/articles/session-affinity.md +++ /dev/null @@ -1,118 +0,0 @@ -# Session Affinity - -## Concept -Session affinity is a mechanism to bind (affinitize) a causally related request sequence to the destination that handled the first request when the load is balanced among several destinations. It is useful in scenarios where the most requests in a sequence work with the same data and the cost of data access differs for different nodes (destinations) handling requests. The most common example is a transient caching (e.g. in-memory) where the first request fetches data from a slower persistent storage into a fast local cache and the others work only with the cached data thus increasing throughput. - -## Configuration -### Services and middleware registration -Session affinity services are registered in the DI container automatically by `AddReverseProxy()`. The middleware `UseSessionAffinity()` is included by default in the parameterless MapReverseProxy method. If you are customizing the proxy pipeline, place the first middleware **before** adding `UseLoadBalancing()`. - -Example: -```C# -app.MapReverseProxy(proxyPipeline => -{ - proxyPipeline.UseSessionAffinity(); - proxyPipeline.UseLoadBalancing(); -}); -``` - -***Note*** Some session affinity implementations depend on Data Protection, which will require additional configuration for scenarios like multiple proxy instances. See [Key Protection](#key-protection) for details. - -### Cluster configuration -Session affinity is configured per cluster according to the following configuration scheme. -```JSON -"ReverseProxy": { - "Clusters": { - "": { - "SessionAffinity": { - "Enabled": "(true|false)", // defaults to 'false' - "Policy": "(HashCookie|ArrCookie|Cookie|CustomHeader)", // defaults to 'HashCookie' - "FailurePolicy": "(Redistribute|Return503Error)", // defaults to 'Redistribute' - "AffinityKeyName": "Key1", - "Cookie": { - "Domain": "localhost", - "Expiration": "03:00:00", - "HttpOnly": true, - "IsEssential": true, - "MaxAge": "1.00:00:00", - "Path": "mypath", - "SameSite": "Strict", - "SecurePolicy": "Always" - } - } - } - } -} -``` - -### Cookie configuration -Attributes for configuring the cookie used with the HashCookie, ArrCookie and Cookie policies can be configured using `SessionAffinityCookieConfig`. The properties can be JSON config as show above or in code as shown below: -```C# -new ClusterConfig -{ - ClusterId = "cluster1", - SessionAffinity = new SessionAffinityConfig - { - Enabled = true, - FailurePolicy = "Return503Error", - Policy = "HashCookie", - AffinityKeyName = "Key1", - Cookie = new SessionAffinityCookieConfig - { - Domain = "mydomain", - Expiration = TimeSpan.FromHours(3), - HttpOnly = true, - IsEssential = true, - MaxAge = TimeSpan.FromDays(1), - Path = "mypath", - SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict, - SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest - } - } -} -``` - -## Affinity Key -Request to destination affinity is established via the affinity key identifying the target destination. That key can be stored on different request parts depending on the given session affinity implementation, but each request cannot have more than one such key. The exact key semantics are implementation dependent, but the built-in policies currently use `DestinationId` as the affinity key. - -The current design doesn't require a key to uniquely identify the single affinitized destination. It's allowed to establish affinity to a destination group. In this case, the exact destination to handle the given request will be determined by the load balancer. - -## Establishing a new affinity or resolution of the existed one -Once a request arrives and gets routed to a cluster with session affinity enabled, the proxy automatically decides whether a new affinity should be established or an existing one needs to be resolved based on the presence and validity of an affinity key on the request as follows: -1. **Request doesn't contain a key**. Resolution is skipped and a new affinity will be establish to the destination chosen by the load balancer - -2. **Affinity key is found on the request and valid**. The affinity mechanism tries to find all healthy destinations matching to the key, and if it finds some it passes the request down the pipeline. If multiple matching destinations are found the load balancer is invoked to choose the single target destination. If only one matching destination is found the load balancer no-ops. - -3. **Affinity key is invalid or no healthy affinitized destinations found**. It's treated as a failure to be handled by a failure policy explained below - -If a new affinity was established for the request, the affinity key gets attached to a response where exact key representation and location depends on the implementation. Currently, there are two built-in policies storing the key on a cookie or custom header. Once the response gets delivered to the client, it's the client responsibility to attach the key to all following requests in the same session. Further, when the next request carrying the key arrives to the proxy, it resolves the existing affinity, but affinity key does not get again attached to the response. Thus, only the first response carries the affinity key. - -There are four built-in affinity polices that format and store the key differently on requests and responses. The default policy is `HashCookie`. -- `HashCookie`, `ArrCookie`, and `Cookie` policies store the key as a cookie, hashed or encrypted respectively, see [Key Protection](#key-protection) below. The request's key will be delivered as a cookie with the configured name and sets the same cookie with `Set-Cookie` header on the first response in an affinitized sequence. The cookie name must be explicitly set via `SessionAffinityConfig.AffinityKeyName`. Other cookie properties can be configured via `SessionAffinityCookieConfig`. -- `CustomHeader` stores the key as an encrypted header. It expects the affinity key to be delivered in a custom header with the configured name and sets the same header on the first response in an affinitized sequence. The header name must be set via `SessionAffinityConfig.AffinityKeyName`. - -**Important**: `AffinityKeyName` must be unique across all clusters with enabled session affinity to avoid conflicts. - -### Key Protection - -The `HashCookie` policy uses the XxHash64 hash to produce a fast, compact, obscured output format for the cookie value. - -The `ArrCookie` policy uses the SHA-256 hash to produce an obscured output for the cookie value that matches IIS's [ARR](https://www.iis.net/downloads/microsoft/application-request-routing) affinity cookie format. ARR uses the destination host name as the input value so YARP's destination ids would need to be configured to match if used in conjunction with ARR. - -`HashCookie` and `ArrCookie` do not provide strong privacy protection and sensitive data should not be included in destination ids. These policies also don't conceal the total number of unique destinations behind the proxy and should not be used if that's a concern. - -The `Cookie` and `CustomHeader` policies encrypt the key using Data Protection. This provides strong privacy protections for the key, but requires [additional configuration](https://learn.microsoft.com/aspnet/core/security/data-protection/configuration/overview) when more than once proxy instance is in use. - -## Affinity failure policy -If the affinity key cannot be decoded or no healthy destination found it's considered as a failure and an affinity failure policy is called to handle it. The policy has the full access to `HttpContext` and can send response to the client by itself. It returns a boolean value indicating whether the request processing can proceed down the pipeline or must be terminated. - -There are two built-in failure policies. The default is `Redistribute`. -1. `Redistribute` - tries to establish a new affinity to one of available healthy destinations by skipping the affinity lookup step and passing all healthy destination to the load balancer the same way it is done for a request without any affinity. Request processing continues. This is implemented by `RedistributeAffinityFailurePolicy`. - -2. `Return503Error` - sends a `503` response back to the client and request processing is terminated. This is implemented by `Return503ErrorAffinityFailurePolicy` - -## Request pipeline -The session affinity mechanisms are implemented by the services (mentioned above) and the two following middleware: -1. `SessionAffinityMiddleware` - coordinates the request's affinity resolution process. First, it calls a policy specified for the given cluster on `ClusterConfig.SessionAffinity.Policy` property. Then, it checks the affinity resolution status returned by the policy, and calls a failure handling policy set on `ClusterConfig.SessionAffinity.FailurePolicy` in case of failures. It must be added to the pipeline **before** the load balancer. - -2. `AffinitizeTransform` - sets the key on the response if a new affinity has been established for the request. Otherwise, if the request follows an existing affinity, it does nothing. This is automatically added as a response transform. diff --git a/docs/docfx/articles/timeouts.md b/docs/docfx/articles/timeouts.md deleted file mode 100644 index 598ae6a54..000000000 --- a/docs/docfx/articles/timeouts.md +++ /dev/null @@ -1,77 +0,0 @@ -# Request Timeouts - -## Introduction - -.NET 8 introduced the [Request Timeouts Middleware](https://learn.microsoft.com/aspnet/core/performance/timeouts) to enable configuring request timeouts globally as well as per endpoint. This functionality is also available in YARP 2.1 when running on .NET 8 or newer. - -## Defaults -Requests do not have any timeouts by default, other than the [Activity Timeout](http-client-config.md#HttpRequest) used to clean up idle requests. A default policy specified in [RequestTimeoutOptions](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.timeouts.requesttimeoutoptions) will apply to proxied requests as well. - -## Configuration -Timeouts and Timeout Policies can be specified per route via [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. - -Timeouts are specified in a TimeSpan HH:MM:SS format. Specifying both Timeout and TimeoutPolicy on the same route is invalid and will cause the configuration to be rejected. - -Note that request timeouts do not apply when a debugger is attached to the process. - -Example: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "TimeoutPolicy": "customPolicy", - "Match": { - "Hosts": [ "localhost" ] - } - } - "route2" : { - "ClusterId": "cluster1", - "Timeout": "00:01:00", - "Match": { - "Hosts": [ "localhost2" ] - } - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/" - } - } - } - } - } -} -``` - -Timeout policies and the default policy can be configured in the service collection and the middleware can be added as follows: -```csharp -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddReverseProxy() - .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); - -builder.Services.AddRequestTimeouts(options => -{ - options.AddPolicy("customPolicy", TimeSpan.FromSeconds(20)); -}); - -var app = builder.Build(); - -app.UseRequestTimeouts(); - -app.MapReverseProxy(); - -app.Run(); -``` - -### Disable timeouts - -Specifying the value `disable` in a route's `TimeoutPolicy` parameter means the request timeout middleware will not apply timeouts to this route. - -### WebSockets - -Request timeouts are disabled after the initial WebSocket handshake. diff --git a/docs/docfx/articles/toc.yml b/docs/docfx/articles/toc.yml deleted file mode 100644 index d005c0b16..000000000 --- a/docs/docfx/articles/toc.yml +++ /dev/null @@ -1,64 +0,0 @@ -- name: Getting Started - href: getting-started.md -- name: Supported Runtimes - href: runtimes.md -- name: Configuration Files - href: config-files.md -- name: Configuration Providers - href: config-providers.md -- name: Configuration Filters - href: config-filters.md -- name: Direct Forwarding - href: direct-forwarding.md -- name: Destination Resolvers - href: destination-resolvers.md -- name: HTTP client configuration - href: http-client-config.md -- name: HTTPS & TLS - href: https-tls.md -- name: Header Guidelines - href: header-guidelines.md -- name: Header Routing - href: header-routing.md -- name: Query Parameter Routing - href: queryparameter-routing.md -- name: Authentication and Authorization - href: authn-authz.md -- name: Rate Limiting - href: rate-limiting.md -- name: Output Caching - href: output-caching.md -- name: Cross-Origin Requests (CORS) - href: cors.md -- name: Session Affinity - href: session-affinity.md -- name: Load Balancing - href: load-balancing.md -- name: Middleware - href: middleware.md -- name: Transforms - href: transforms.md -- name: Destinations Health Checks - href: dests-health-checks.md -- name: Distributed Tracing - href: distributed-tracing.md -- name: gRPC - href: grpc.md -- name: WebSockets and SPDY - href: websockets.md -- name: Timeouts - href: timeouts.md -- name: Service Fabric Integration - href: service-fabric-int.md -- name: Http.sys Delegation - href: httpsys-delegation.md -- name: Packages references - href: packages-refs.md -- name: Diagnosing proxy issues - href: diagnosing-yarp-issues.md -- name: A/B Testing - href: ab-testing.md -- name: HTTP/3 - href: http3.md -- name: Lets Encrypt - href: lets-encrypt.md diff --git a/docs/docfx/articles/transforms.md b/docs/docfx/articles/transforms.md deleted file mode 100644 index 1bf2344bf..000000000 --- a/docs/docfx/articles/transforms.md +++ /dev/null @@ -1,1203 +0,0 @@ -# Request and Response Transforms - -## Introduction -When proxying a request it's common to modify parts of the request or response to adapt to the destination server's requirements or to flow additional data such as the client's original IP address. This process is implemented via Transforms. Types of transforms are defined globally for the application and then individual routes supply the parameters to enable and configure those transforms. The original request objects are not modified by these transforms, only the proxy requests. - -Request and response body transforms are not provided by YARP but you can write middleware to do this. - -## Defaults -The following transforms are enabled by default for all routes. They can be configured or disabled as shown later in this document. -- Host - Suppress the incoming request's Host header. The proxy request will default to the host name specified in the destination server address. See [RequestHeaderOriginalHost](#requestheaderoriginalhost) below. -- X-Forwarded-For - Sets the client's IP address to the X-Forwarded-For header. See [X-Forwarded](#x-forwarded) below. -- X-Forwarded-Proto - Sets the request's original scheme (http/https) to the X-Forwarded-Proto header. See [X-Forwarded](#x-forwarded) below. -- X-Forwarded-Host - Sets the request's original Host to the X-Forwarded-Host header. See [X-Forwarded](#x-forwarded) below. -- X-Forwarded-Prefix - Sets the request's original PathBase, if any, to the X-Forwarded-Prefix header. See [X-Forwarded](#x-forwarded) below. - -For example the following incoming request to `http://IncomingHost:5000/path`: -``` -GET /path HTTP/1.1 -Host: IncomingHost:5000 -Accept: */* -header1: foo -``` -would be transformed and proxied to the destination server `https://DestinationHost:6000/` as follows using these defaults: -``` -GET /path HTTP/1.1 -Host: DestinationHost:6000 -Accept: */* -header1: foo -X-Forwarded-For: 5.5.5.5 -X-Forwarded-Proto: http -X-Forwarded-Host: IncomingHost:5000 -``` - -## Transform Categories - -Transforms fall into a few categories: request, response, and response trailers. Request trailers are not supported because they are not supported by the underlying HttpClient. - -## Adding transforms - -Transforms can be added to routes either through configuration or programmatically. - -### From Configuration - -Transforms can be configured on [RouteConfig.Transforms](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. These can be modified and reloaded without restarting the proxy. A transform is configured using one or more key-value string pairs. - -Here is an example of common transforms: -```JSON -{ - "ReverseProxy": { - "Routes": { - "route1" : { - "ClusterId": "cluster1", - "Match": { - "Hosts": [ "localhost" ] - }, - "Transforms": [ - { "PathPrefix": "/apis" }, - { - "RequestHeader": "header1", - "Append": "bar" - }, - { - "ResponseHeader": "header2", - "Append": "bar", - "When": "Always" - }, - { "ClientCert": "X-Client-Cert" }, - { "RequestHeadersCopy": "true" }, - { "RequestHeaderOriginalHost": "true" }, - { - "X-Forwarded": "Append", - "HeaderPrefix": "X-Forwarded-" - } - ] - }, - "route2" : { - "ClusterId": "cluster1", - "Match": { - "Path": "/api/{plugin}/stuff/{**remainder}" - }, - "Transforms": [ - { "PathPattern": "/foo/{plugin}/bar/{**remainder}" }, - { - "QueryValueParameter": "q", - "Append": "plugin" - } - ] - } - }, - "Clusters": { - "cluster1": { - "Destinations": { - "cluster1/destination1": { - "Address": "https://localhost:10001/Path/Base" - } - } - } - } - } -} -``` - -All configuration entries are treated as case-insensitive, though the destination server may treat the resulting values as case sensitive or insensitive such as the path. - -The details for these transforms are covered later in this document. - -Developers that want to integrate their custom transforms with the `Transforms` section of configuration can do so using [ITransformFactory](#itransformfactory) described below. - -### From Code - -Transforms can be added to routes programmatically by calling the [AddTransforms](xref:Microsoft.Extensions.DependencyInjection.ReverseProxyServiceCollectionExtensions.AddTransforms*) method. - -`AddTransforms` can be called after `AddReverseProxy` to provide a callback for configuring transforms. This callback is invoked each time a route is built or rebuilt and allows the developer to inspect the [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig) information and conditionally add transforms for it. - -The `AddTransforms` callback provides a [TransformBuilderContext](xref:Yarp.ReverseProxy.Transforms.Builder.TransformBuilderContext) where transforms can be added or configured. Most transforms provide `TransformBuilderContext` extension methods to make them easier to add. These are extensions documented below with the individual transform descriptions. - -The `TransformBuilderContext` also includes an `IServiceProvider` for access to any needed services. - -```C# -services.AddReverseProxy() - .LoadFromConfig(_configuration.GetSection("ReverseProxy")) - .AddTransforms(builderContext => - { - // Added to all routes. - builderContext.AddPathPrefix("/prefix"); - - // Conditionally add a transform for routes that require auth. - if (!string.IsNullOrEmpty(builderContext.Route.AuthorizationPolicy)) - { - builderContext.AddRequestTransform(async transformContext => - { - transformContext.ProxyRequest.Headers.Add("CustomHeader", "CustomValue"); - }); - } - }); -``` - -For more advanced control see [ITransformProvider](#itransformprovider) described below. - -## Request transforms - -Request transforms include the request path, query, HTTP version, method, and headers. In code these are represented by the [RequestTransformContext](xref:Yarp.ReverseProxy.Transforms.RequestTransformContext) object and processed by implementations of the abstract class [RequestTransform](xref:Yarp.ReverseProxy.Transforms.RequestTransform). - -Notes: -- The proxy request scheme (http/https), authority, and path base, are taken from the destination server address (`https://localhost:10001/Path/Base` in the example above) and should not be modified by transforms. -- The Host header can be overridden by transforms independent of the authority, see [RequestHeader](#requestheader) below. -- The request's original PathBase property is not used when constructing the proxy request, see [X-Forwarded](#x-forwarded). -- All incoming request headers are copied to the proxy request by default with the exception of the Host header (see [Defaults](#defaults)). [X-Forwarded](#x-forwarded) headers are also added by default. These behaviors can be configured using the following transforms. Additional request headers can be specified, or request headers can be excluded by setting them to an empty value. - -The following are built in transforms identified by their primary config key. These transforms are applied in the order they are specified in the route configuration. - -### PathPrefix - -**Modifies the request path adding a prefix value** - -| Key | Value | Required | -|-----|-------|----------| -| PathPrefix | A path starting with a '/' | yes | - -Config: -```JSON -{ "PathPrefix": "/prefix" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformPathPrefix(prefix: "/prefix"); -``` -```C# -transformBuilderContext.AddPathPrefix(prefix: "/prefix"); -``` -Example:
-`/request/path` becomes `/prefix/request/path` - -This will prefix the request path with the given value. - -### PathRemovePrefix - -**Modifies the request path removing a prefix value** - -| Key | Value | Required | -|-----|-------|----------| -| PathRemovePrefix | A path starting with a '/' | yes | - -Config: -```JSON -{ "PathRemovePrefix": "/prefix" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformPathRemovePrefix(prefix: "/prefix"); -``` -```csharp -transformBuilderContext.AddPathRemovePrefix(prefix: "/prefix"); -``` -Example:
-`/prefix/request/path` becomes `/request/path`
-`/prefix2/request/path` is not modified
- -This will remove the matching prefix from the request path. Matches are made on path segment boundaries (`/`). If the prefix does not match then no changes are made. - -### PathSet - -**Replaces the request path with the specified value** - -| Key | Value | Required | -|-----|-------|----------| -| PathSet | A path starting with a '/' | yes | - -Config: -```JSON -{ "PathSet": "/newpath" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformPathSet(path: "/newpath"); -``` -```C# -transformBuilderContext.AddPathSet(path: "/newpath"); -``` -Example:
-`/request/path` becomes `/newpath` - -This will set the request path with the given value. - -### PathPattern - -**Replaces the request path using a pattern template** - -| Key | Value | Required | -|-----|-------|----------| -| PathPattern | A path template starting with a '/' | yes | - -Config: -```JSON -{ "PathPattern": "/my/{plugin}/api/{**remainder}" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformPathRouteValues(pattern: new PathString("/my/{plugin}/api/{**remainder}")); -``` -```C# -transformBuilderContext.AddPathRouteValues(pattern: new PathString("/my/{plugin}/api/{**remainder}")); -``` - -This will set the request path with the given value and replace any `{}` segments with the associated route value. `{}` segments without a matching route value are removed. The final `{}` segment can be marked as `{**remainder}` to indicate this is a catch-all segment that may contain multiple path segments. See ASP.NET Core's [routing docs](https://docs.microsoft.com/aspnet/core/fundamentals/routing#route-template-reference) for more information about route templates. - -Example: - -| Step | Value | -|------|-------| -| Route definition | `/api/{plugin}/stuff/{**remainder}` | -| Request path | `/api/v1/stuff/more/stuff` | -| Plugin value | `v1` | -| Remainder value | `more/stuff` | -| PathPattern | `/my/{plugin}/api/{**remainder}` | -| Result | `/my/v1/api/more/stuff` | - -### QueryValueParameter - -**Adds or replaces parameters in the request query string** - -| Key | Value | Required | -|-----|-------|----------| -| QueryValueParameter | Name of a query string parameter | yes | -| Set/Append | Static value | yes | - -Config: -```JSON -{ - "QueryValueParameter": "foo", - "Append": "bar" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformQueryValue(queryKey: "foo", value: "bar", append: true); -``` -```C# -transformBuilderContext.AddQueryValue(queryKey: "foo", value: "bar", append: true); -``` - -This will add a query string parameter with the name `foo` and sets it to the static value `bar`. - -Example: - -| Step | Value | -|------|-------| -| Query | `?a=b` | -| QueryValueParameter | `foo` | -| Append | `remainder` | -| Result | `?a=b&foo=remainder` | - -### QueryRouteParameter - -**Adds or replaces a query string parameter with a value from the route configuration** - -| Key | Value | Required | -|-----|-------|----------| -| QueryRouteParameter | Name of a query string parameter | yes | -| Set/Append | The name of a route value | yes | - -Config: -```JSON -{ - "QueryRouteParameter": "foo", - "Append": "remainder" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformQueryRouteValue(queryKey: "foo", routeValueKey: "remainder", append: true); -``` -```C# -transformBuilderContext.AddQueryRouteValue(queryKey: "foo", routeValueKey: "remainder", append: true); -``` - -This will add a query string parameter with the name `foo` and sets it to the value of the associated route value. - -Example: - -| Step | Value | -|------|-------| -| Route definition | `/api/{*remainder}` | -| Request path | `/api/more/stuff` | -| Remainder value | `more/stuff` | -| QueryRouteParameter | `foo` | -| Append | `remainder` | -| Result | `?foo=more/stuff` | - -### QueryRemoveParameter - -**Removes the specified parameter from the request query string** - -| Key | Value | Required | -|-----|-------|----------| -| QueryRemoveParameter | Name of a query string parameter | yes | - -Config: -```JSON -{ "QueryRemoveParameter": "foo" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformQueryRemoveKey(queryKey: "foo"); -``` -```C# -transformBuilderContext.AddQueryRemoveKey(queryKey: "foo"); -``` - -This will remove a query string parameter with the name `foo` if present on the request. - -Example: - -| Step | Value | -|------|-------| -| Request path | `?a=b&foo=c` | -| QueryRemoveParameter | `foo` | -| Result | `?a=b` | - -### HttpMethodChange - -**Changes the http method used in the request** - -| Key | Value | Required | -|-----|-------|----------| -| HttpMethodChange | The http method to replace | yes | -| Set | The new http method | yes | - -Config: -```JSON -{ - "HttpMethodChange": "PUT", - "Set": "POST" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformHttpMethodChange(fromHttpMethod: HttpMethods.Put, toHttpMethod: HttpMethods.Post); -``` -```C# -transformBuilderContext.AddHttpMethodChange(fromHttpMethod: HttpMethods.Put, toHttpMethod: HttpMethods.Post); -``` - -This will change PUT requests to POST. - -### RequestHeadersCopy - -**Sets whether incoming request headers are copied to the outbound request** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| RequestHeadersCopy | true/false | true | yes | - -Config: -```JSON -{ "RequestHeadersCopy": "false" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformCopyRequestHeaders(copy: false); -``` -```C# -transformBuilderContext.CopyRequestHeaders = false; -``` - -This sets if all incoming request headers are copied to the proxy request. This setting is enabled by default and can by disabled by configuring the transform with a `false` value. Transforms that reference specific headers will still be run if this is disabled. - -### RequestHeaderOriginalHost - -**Specifies if the incoming request Host header should be copied to the proxy request** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| RequestHeaderOriginalHost | true/false | false | yes | - -Config: -```JSON -{ "RequestHeaderOriginalHost": "true" } -``` -```csharp -routeConfig = routeConfig.WithTransformUseOriginalHostHeader(useOriginal: true); -``` -```C# -transformBuilderContext.AddOriginalHost(true); -``` - -This specifies if the incoming request Host header should be copied to the proxy request. This setting is disabled by default and can be enabled by configuring the transform with a `true` value. Transforms that directly reference the `Host` header will override this transform. - -### RequestHeader - -**Adds or replaces request headers** - -| Key | Value | Required | -|-----|-------|----------| -| RequestHeader | The header name | yes | -| Set/Append | The header value | yes | - -Config: -```JSON -{ - "RequestHeader": "MyHeader", - "Set": "MyValue" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformRequestHeader(headerName: "MyHeader", value: "MyValue", append: false); -``` -```C# -transformBuilderContext.AddRequestHeader(headerName: "MyHeader", value: "MyValue", append: false); -``` - -Example: -``` -MyHeader: MyValue -``` - -This sets or appends the value for the named header. Set replaces any existing header. Append adds an additional header with the given value. -Note: setting "" as a header value is not recommended and can cause an undefined behavior. - -### RequestHeaderRouteValue - -**Adds or replaces a header with a value from the route configuration** - -| Key | Value | Required | -|-----|-------|----------| -| RequestHeader | Name of a query string parameter | yes | -| Set/Append | The name of a route value | yes | - -Config: -```JSON -{ - "RequestHeaderRouteValue": "MyHeader", - "Set": "MyRouteKey" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformRequestHeaderRouteValue(headerName: "MyHeader", routeValueKey: "key", append: false); -``` -```C# -transformBuilderContext.AddRequestHeaderRouteValue(headerName: "MyHeader", routeValueKey: "key", append: false); -``` - -Example: - -| Step | Value | -|------|---------------------| -| Route definition | `/api/{*remainder}` | -| Request path | `/api/more/stuff` | -| Remainder value | `more/stuff` | -| RequestHeaderFromRoute | `foo` | -| Append | `remainder` | -| Result | `foo: more/stuff` | - -This sets or appends the value for the named header with a value from the route configuration. Set replaces any existing header. Append adds an additional header with the given value. -Note: setting "" as a header value is not recommended and can cause an undefined behavior. - -### RequestHeaderRemove - -**Removes request headers** - -| Key | Value | Required | -|-----|-------|----------| -| RequestHeaderRemove | The header name | yes | - -Config: -```JSON -{ - "RequestHeaderRemove": "MyHeader" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformRequestHeaderRemove(headerName: "MyHeader"); -``` -```C# -transformBuilderContext.AddRequestHeaderRemove(headerName: "MyHeader"); -``` - -Example: -``` -MyHeader: MyValue -AnotherHeader: AnotherValue -``` - -This removes the named header. - -### RequestHeadersAllowed - -| Key | Value | Required | -|-----|-------|----------| -| RequestHeadersAllowed | A semicolon separated list of allowed header names. | yes | - -Config: -```JSON -{ - "RequestHeadersAllowed": "Header1;header2" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformRequestHeadersAllowed("Header1", "header2"); -``` -```C# -transformBuilderContext.AddRequestHeadersAllowed("Header1", "header2"); -``` - -YARP copies most request headers to the proxy request by default (see [RequestHeadersCopy](transforms.md#requestheaderscopy)). Some security models only allow specific headers to be proxied. This transform disables RequestHeadersCopy and only copies the given headers. Other transforms that modify or append to existing headers may be affected if not included in the allow list. - -Note that there are some headers YARP does not copy by default since they are connection specific or otherwise security sensitive (e.g. `Connection`, `Alt-Svc`). Putting those header names in the allow list will bypass that restriction but is strongly discouraged as it may negatively affect the functionality of the proxy or cause security vulnerabilities. - -Example: -``` -Header1: value1 -Header2: value2 -AnotherHeader: AnotherValue -``` - -Only header1 and header2 are copied to the proxy request. - -### X-Forwarded - -**Adds headers with information about the original client request** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| X-Forwarded | Default action (Set, Append, Remove, Off) to apply to all X-Forwarded-* listed below | Set | yes | -| For | Action to apply to this header | * See X-Forwarded | no | -| Proto | Action to apply to this header | * See X-Forwarded | no | -| Host | Action to apply to this header | * See X-Forwarded | no | -| Prefix | Action to apply to this header | * See X-Forwarded | no | -| HeaderPrefix | The header name prefix | "X-Forwarded-" | no | - -Action "Off" completely disables the transform. - -Config: -```JSON -{ - "X-Forwarded": "Set", - "For": "Remove", - "Proto": "Append", - "Prefix": "Off", - "HeaderPrefix": "X-Forwarded-" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformXForwarded( - headerPrefix = "X-Forwarded-", - ForwardedTransformActions xDefault = ForwardedTransformActions.Set, - ForwardedTransformActions? xFor = null, - ForwardedTransformActions? xHost = null, - ForwardedTransformActions? xProto = null, - ForwardedTransformActions? xPrefix = null); -``` -```C# -transformBuilderContext.AddXForwarded(ForwardedTransformActions.Set); -transformBuilderContext.AddXForwardedFor(headerName: "X-Forwarded-For", ForwardedTransformActions.Append); -transformBuilderContext.AddXForwardedHost(headerName: "X-Forwarded-Host", ForwardedTransformActions.Append); -transformBuilderContext.AddXForwardedProto(headerName: "X-Forwarded-Proto", ForwardedTransformActions.Off); -transformBuilderContext.AddXForwardedPrefix(headerName: "X-Forwarded-Prefix", ForwardedTransformActions.Remove); -``` - -Example: -``` -X-Forwarded-For: 5.5.5.5 -X-Forwarded-Proto: https -X-Forwarded-Host: IncomingHost:5000 -X-Forwarded-Prefix: /path/base -``` -Disable default headers: -```JSON -{ "X-Forwarded": "Off" } -``` -```C# -transformBuilderContext.UseDefaultForwarders = false; -``` - -When the proxy connects to the destination server, the connection is indepenent from the one the client made to the proxy. The destination server likely needs original connection information for security checks and to properly generate absolute URIs for links and redirects. To enable information about the client connection to be passed to the destination a set of extra headers can be added. Until the `Forwarded` standard was created, a common solution is to use `X-Forwarded-*` headers. There is no official standard that defines the `X-Forwarded-*` headers and implementations vary, check your destination server for support. - -This transform is enabled by default even if not specified in the route config. - -Set the `X-Forwarded` value to a comma separated list containing the headers you need to enable. All for headers are enabled by default. All can be disabled by specifying the value `"Off"`. - - -The Prefix specifies the header name prefix to use for each header. With the default `X-Forwarded-` prefix the resulting headers will be `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Prefix`. - -Transform action specifies how each header should be combined with an existing header of the same name. It can be "Set", "Append", "Remove, or "Off" (completely disable the transform). A request traversing multiple proxies may accumulate a list of such headers and the destination server will need to evaluate the list to determine the original value. If action is "Set" and the associated value is not available on the request (e.g. RemoteIpAddress is null), any existing header is still removed to prevent spoofing. - -The {Prefix}For header value is taken from `HttpContext.Connection.RemoteIpAddress` representing the prior caller's IP address. The port is not included. IPv6 addresses do not include the bounding `[]` brackets. - -The {Prefix}Proto header value is taken from `HttpContext.Request.Scheme` indicating if the prior caller used HTTP or HTTPS. - -The {Prefix}Host header value is taken from the incoming request's Host header. This is independent of RequestHeaderOriginalHost specified above. Unicode/IDN hosts are punycode encoded. - -The {Prefix}Prefix header value is taken from `HttpContext.Request.PathBase`. The PathBase property is not used when generating the proxy request so the destination server will need the original value to correctly generate links and directs. The value is in the percent encoded Uri format. - -### Forwarded - -**Adds a header with information about the original client request** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| Forwarded | A comma separated list containing any of these values: for,by,proto,host | (none) | yes | -| ForFormat | Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/UnknownAndRandomPort/Ip/IpAndPort/IpAndRandomPort | Random | no | -| ByFormat | Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/UnknownAndRandomPort/Ip/IpAndPort/IpAndRandomPort | Random | no | -| Action | Action to apply to this header (Set, Append, Remove, Off) | Set | no | - -Config: -```JSON -{ - "Forwarded": "by,for,host,proto", - "ByFormat": "Random", - "ForFormat": "IpAndPort", - "Action": "Append" -}, -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformForwarded(useHost: true, useProto: true, forFormat: NodeFormat.IpAndPort, ByFormat: NodeFormat.Random, action: ForwardedTransformAction.Append); -``` -```C# -transformBuilderContext.AddForwarded(useHost: true, useProto: true, forFormat: NodeFormat.IpAndPort, ByFormat: NodeFormat.Random, action: ForwardedTransformAction.Append); -``` -Example: -``` -Forwarded: proto=https;host="localhost:5001";for="[::1]:20173";by=_YQuN68tm6 -``` - -The `Forwarded` header is defined by [RFC 7239](https://tools.ietf.org/html/rfc7239). It consolidates many of the same functions as the unofficial X-Forwarded headers, flowing information to the destination server that would otherwise be obscured by using a proxy. - -Enabling this transform will disable the default X-Forwarded transforms as they carry similar information in another format. The X-Forwarded transforms can still be explicitly enabled. - -Action: This specifies how the transform should handle an existing Forwarded header. It can be "Set", "Append", "Remove, or "Off" (completely disable the transform). A request traversing multiple proxies may accumulate a list of such headers and the destination server will need to evaluate the list to determine the original value. - -Proto: This value is taken from `HttpContext.Request.Scheme` indicating if the prior caller used HTTP or HTTPS. - -Host: This value is taken from the incoming request's Host header. This is independent of RequestHeaderOriginalHost specified above. Unicode/IDN hosts are punycode encoded. - -For: This value identifies the prior caller. IP addresses are taken from `HttpContext.Connection.RemoteIpAddress`. See ByFormat and ForFormat below for details. - -By: This value identifies where the proxy received the request. IP addresses are taken from `HttpContext.Connection.LocalIpAddress`. See ByFormat and ForFormat below for details. - -ByFormat and ForFormat: - -The RFC allows a [variety of formats](https://tools.ietf.org/html/rfc7239#section-6) for the By and For fields. It requires that the default format uses an obfuscated identifier identified here as Random. - -| Format | Description | Example | -|--------|-------------|---------| -| Random | An obfuscated identifier that is generated randomly per request. This allows for diagnostic tracing scenarios while limiting the flow of uniquely identifying information for privacy reasons. | `by=_YQuN68tm6` | -| RandomAndPort | The Random identifier plus the port. | `by="_YQuN68tm6:80"` | -| RandomAndRandomPort | The Random identifier plus another random identifier for the port. | `by="_YQuN68tm6:_jDw5Cf3tQ"` | -| Unknown | This can be used when the identity of the preceding entity is not known, but the proxy server still wants to signal that the request was forwarded. | `by=unknown` | -| UnknownAndPort | The Unknown identifier plus the port if available. | `by="unknown:80"` | -| UnknownAndRandomPort | The Unknown identifier plus random identifier for the port. | `by="unknown:_jDw5Cf3tQ"` | -| Ip | An IPv4 address or an IPv6 address including brackets. | `by="[::1]"` | -| IpAndPort | The IP address plus the port. | `by="[::1]:80"` | -| IpAndRandomPort | The IP address plus random identifier for the port. | `by="[::1]:_jDw5Cf3tQ"` | - -### ClientCert - -**Forwards the client cert used on the inbound connection as a header to destination** - -| Key | Value | Required | -|-----|-------|----------| -| ClientCert | The header name | yes | - -Config: -```JSON -{ "ClientCert": "X-Client-Cert" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformClientCertHeader(headerName: "X-Client-Cert"); -``` -```C# -transformBuilderContext.AddClientCertHeader(headerName: "X-Client-Cert"); -``` -Example: -``` -X-Client-Cert: SSdtIGEgY2VydGlmaWNhdGU... -``` -As the inbound and outbound connections are independent, there needs to be a way to pass any inbound client certificate to the destination server. This transform causes the client certificate taken from `HttpContext.Connection.ClientCertificate` to be Base64 encoded and set as the value for the given header name. The destination server may need that certificate to authenticate the client. There is no standard that defines this header and implementations vary, check your destination server for support. - -Servers do minimal validation on the incoming client certificate by default. The certificate should be validated either in the proxy or the destination, see the [client certificate auth](https://docs.microsoft.com/aspnet/core/security/authentication/certauth) docs for details. - -This transform will only apply if the client certificate is already present on the connection. See the [optional certs doc](https://docs.microsoft.com/aspnet/core/security/authentication/certauth#optional-client-certificates) if it needs to be requested from the client on a per-route basis. - -## Response and Response Trailers - -All response headers and trailers are copied from the proxied response to the outgoing client response by default. Response and response trailer transforms may specify if they should be applied only for successful responses or for all responses. - -In code these are implemented as derivations of the abstract classes [ResponseTransform](xref:Yarp.ReverseProxy.Transforms.ResponseTransform) and [ResponseTrailersTransform](xref:Yarp.ReverseProxy.Transforms.ResponseTrailersTransform). - -### ResponseHeadersCopy - -**Sets whether destination response headers are copied to the client** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseHeadersCopy | true/false | true | yes | - -Config: -```JSON -{ "ResponseHeadersCopy": "false" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformCopyResponseHeaders(copy: false); -``` -```C# -transformBuilderContext.CopyResponseHeaders = false; -``` - -This sets if all proxy response headers are copied to the client response. This setting is enabled by default and can be disabled by configuring the transform with a `false` value. Transforms that reference specific headers will still be run if this is disabled. - -### ResponseHeader - -**Adds or replaces response headers** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseHeader | The header name | (none) | yes | -| Set/Append | The header value | (none) | yes | -| When | Success/Always/Failure | Success | no | - -Config: -```JSON -{ - "ResponseHeader": "HeaderName", - "Append": "value", - "When": "Success" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseHeader(headerName: "HeaderName", value: "value", append: true, ResponseCondition.Success); -``` -```C# -transformBuilderContext.AddResponseHeader(headerName: "HeaderName", value: "value", append: true, always: ResponseCondition.Success); -``` -Example: -``` -HeaderName: value -``` - -This sets or appends the value for the named response header. Set replaces any existing header. Append adds an additional header with the given value. -Note: setting "" as a header value is not recommended and can cause an undefined behavior. - -`When` specifies if the response header should be included for all, successful, or failure responses. Any response with a status code less than 400 is considered a success. - -### ResponseHeaderRemove - -**Removes response headers** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseHeaderRemove | The header name | (none) | yes | -| When | Success/Always/Failure | Success | no | - -Config: -```JSON -{ - "ResponseHeaderRemove": "HeaderName", - "When": "Success" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseHeaderRemove(headerName: "HeaderName", ResponseCondition.Success); -``` -```C# -transformBuilderContext.AddResponseHeaderRemove(headerName: "HeaderName", ResponseCondition.Success); -``` -Example: -``` -HeaderName: value -AnotherHeader: another-value -``` - -This removes the named response header. - -`When` specifies if the response header should be removed for all, successful, or failure responses. Any response with a status code less than 400 is considered a success. - -### ResponseHeadersAllowed - -| Key | Value | Required | -|-----|-------|----------| -| ResponseHeadersAllowed | A semicolon separated list of allowed header names. | yes | - -Config: -```JSON -{ - "ResponseHeadersAllowed": "Header1;header2" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseHeadersAllowed("Header1", "header2"); -``` -```C# -transformBuilderContext.AddResponseHeadersAllowed("Header1", "header2"); -``` - -YARP copies most response headers from the proxy response by default (see [ResponseHeadersCopy](transforms.md#responseheaderscopy)). Some security models only allow specific headers to be proxied. This transform disables ResponseHeadersCopy and only copies the given headers. Other transforms that modify or append to existing headers may be affected if not included in the allow list. - -Note that there are some headers YARP does not copy by default since they are connection specific or otherwise security sensitive (e.g. `Connection`, `Alt-Svc`). Putting those header names in the allow list will bypass that restriction but is strongly discouraged as it may negatively affect the functionality of the proxy or cause security vulnerabilities. - -Example: -``` -Header1: value1 -Header2: value2 -AnotherHeader: AnotherValue -``` - -Only header1 and header2 are copied from the proxy response. - -### ResponseTrailersCopy - -**Sets whether destination trailing response headers are copied to the client** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseTrailersCopy | true/false | true | yes | - -Config: -```JSON -{ "ResponseTrailersCopy": "false" } -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformCopyResponseTrailers(copy: false); -``` -```C# -transformBuilderContext.CopyResponseTrailers = false; -``` - -This sets if all proxy response trailers are copied to the client response. This setting is enabled by default and can be disabled by configuring the transform with a `false` value. Transforms that reference specific headers will still be run if this is disabled. - -### ResponseTrailer - -**Adds or replaces trailing response headers** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseTrailer | The header name | (none) | yes | -| Set/Append | The header value | (none) | yes | -| When | Success/Always/Failure | Success | no | - -Config: -```JSON -{ - "ResponseTrailer": "HeaderName", - "Append": "value", - "When": "Success" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseTrailer(headerName: "HeaderName", value: "value", append: true, ResponseCondition.Success); -``` -```C# -transformBuilderContext.AddResponseTrailer(headerName: "HeaderName", value: "value", append: true, ResponseCondition.Success); -``` -Example: -``` -HeaderName: value -``` - -Response trailers are headers sent at the end of the response body. Support for trailers is uncommon in HTTP/1.1 implementations but is becoming common in HTTP/2 implementations. Check your client and server for support. - -ResponseTrailer follows the same structure and guidance as [ResponseHeader](transforms.md#responseheader). - -### ResponseTrailerRemove - -**Removes trailing response headers** - -| Key | Value | Default | Required | -|-----|-------|---------|----------| -| ResponseTrailerRemove | The header name | (none) | yes | -| When | Success/Always/Failure | Success | no | - -Config: -```JSON -{ - "ResponseTrailerRemove": "HeaderName", - "When": "Success" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseTrailerRemove(headerName: "HeaderName", ResponseCondition.Success); -``` -```C# -transformBuilderContext.AddResponseTrailerRemove(headerName: "HeaderName", ResponseCondition.Success); -``` -Example: -``` -HeaderName: value -AnotherHeader: another-value -``` - -This removes the named trailing header. - -ResponseTrailerRemove follows the same structure and guidance as [ResponseHeaderRemove](transforms.md#responseheaderremove). - -### ResponseTrailersAllowed - -| Key | Value | Required | -|-----|-------|----------| -| ResponseTrailersAllowed | A semicolon separated list of allowed header names. | yes | - -Config: -```JSON -{ - "ResponseTrailersAllowed": "Header1;header2" -} -``` -Code: -```csharp -routeConfig = routeConfig.WithTransformResponseTrailersAllowed("Header1", "header2"); -``` -```C# -transformBuilderContext.AddResponseTrailersAllowed("Header1", "header2"); -``` - -YARP copies most response trailers from the proxy response by default (see [ResponseTrailersCopy](transforms.md#responsetrailerscopy)). Some security models only allow specific headers to be proxied. This transform disables ResponseTrailersCopy and only copies the given headers. Other transforms that modify or append to existing headers may be affected if not included in the allow list. - -Note that there are some headers YARP does not copy by default since they are connection specific or otherwise security sensitive (e.g. `Connection`, `Alt-Svc`). Putting those header names in the allow list will bypass that restriction but is strongly discouraged as it may negatively affect the functionality of the proxy or cause security vulnerabilities. - -Example: -``` -Header1: value1 -Header2: value2 -AnotherHeader: AnotherValue -``` - -Only header1 and header2 are copied from the proxy response. - -## Extensibility - -### AddRequestTransform - -[AddRequestTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddRequestTransform*) is a `TransformBuilderContext` extension method that defines a request transform as a `Func`. This allows creating a custom request transform without implementing a `RequestTransform` derived class. - -### AddResponseTransform - -[AddResponseTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTransform*) is a `TransformBuilderContext` extension method that defines a response transform as a `Func`. This allows creating a custom response transform without implementing a `ResponseTransform` derived class. - -### AddResponseTrailersTransform - -[AddResponseTrailersTransform](xref:Yarp.ReverseProxy.Transforms.TransformBuilderContextFuncExtensions.AddResponseTrailersTransform*) is a `TransformBuilderContext` extension method that defines a response trailers transform as a `Func`. This allows creating a custom response trailers transform without implementing a `ResponseTrailersTransform` derived class. - -### RequestTransform - -All request transforms must derive from the abstract base class [RequestTransform](xref:Yarp.ReverseProxy.Transforms.RequestTransform). These can freely modify the proxy `HttpRequestMessage`. Avoid reading or modifying the request body as this may disrupt the proxying flow. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use. - -A request transform may conditionally produce an immediate response such as for error conditions. This prevents any remaining transforms from running and the request from being proxied. This is indicated by setting the `HttpResponse.StatusCode` to a value other than 200, or calling `HttpResponse.StartAsync()`, or writing to the `HttpResponse.Body` or `BodyWriter`. - -### ResponseTransform - -All response transforms must derive from the abstract base class [ResponseTransform](xref:Yarp.ReverseProxy.Transforms.ResponseTransform). These can freely modify the client `HttpResponse`. Avoid reading or modifying the response body as this may disrupt the proxying flow. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use. - -### ResponseTrailersTransform - -All response trailers transforms must derive from the abstract base class [ResponseTrailersTransform](xref:Yarp.ReverseProxy.Transforms.ResponseTrailersTransform). These can freely modify the client HttpResponse trailers. These run after the response body and should not attempt to modify the response headers or body. Consider also adding a parametrized extension method on `TransformBuilderContext` for discoverability and easy of use. - -### Request body transforms - -YARP does not provide any built in transforms for modifying the request body. However, the body can be modified in custom transforms. - -Be careful about which kinds of requests are modified, how much data gets buffered, enforcing timeouts, parsing untrusted input, and updating the body-related headers like `Content-Length`. - -The below example uses simple, inefficient buffering to transform requests. A more efficient implementation would wrap and replace `HttpContext.Request.Body` with a stream that performed the needed modifications as data was proxied from client to server. That would also require removing the Content-Length header since the final length would not be known in advance. - -This sample requires YARP 1.1, see https://github.com/microsoft/reverse-proxy/pull/1569. - -```C# -.AddTransforms(context => -{ - context.AddRequestTransform(async requestContext => - { - using var reader = new StreamReader(requestContext.HttpContext.Request.Body); - // TODO: size limits, timeouts - var body = await reader.ReadToEndAsync(); - if (!string.IsNullOrEmpty(body)) - { - body = body.Replace("Alpha", "Charlie"); - var bytes = Encoding.UTF8.GetBytes(body); - // Change Content-Length to match the modified body, or remove it. - requestContext.HttpContext.Request.Body = new MemoryStream(bytes); - // Request headers are copied before transforms are invoked, update any needed headers on the ProxyRequest - requestContext.ProxyRequest.Content.Headers.ContentLength = bytes.Length; - } - }); -}); -``` - -### Response body transforms - -YARP does not provide any built in transforms for modifying the response body. However, the body can be modified in custom transforms. - -Be careful about which kinds of responses are modified, how much data gets buffered, enforcing timeouts, parsing untrusted input, and updating the body-related headers like `Content-Length`. You may need to decompress content before modifying it, as indicated by the Content-Encoding header, and afterwards re-compress it or remove the header. - -The below example uses simple, inefficient buffering to transform responses. A more efficient implementation would wrap the stream returned by `ReadAsStreamAsync()` with a stream that performed the needed modifications as data was proxied from client to server. That would also require removing the Content-Length header since the final length would not be known in advance. - -```C# -.AddTransforms(context => -{ - context.AddResponseTransform(async responseContext => - { - var stream = await responseContext.ProxyResponse.Content.ReadAsStreamAsync(); - using var reader = new StreamReader(stream); - // TODO: size limits, timeouts - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrEmpty(body)) - { - responseContext.SuppressResponseBody = true; - - body = body.Replace("Bravo", "Charlie"); - var bytes = Encoding.UTF8.GetBytes(body); - // Change Content-Length to match the modified body, or remove it. - responseContext.HttpContext.Response.ContentLength = bytes.Length; - // Response headers are copied before transforms are invoked, update any needed headers on the HttpContext.Response. - await responseContext.HttpContext.Response.Body.WriteAsync(bytes); - } - }); -}); -``` - -### ITransformProvider - -[ITransformProvider](xref:Yarp.ReverseProxy.Transforms.Builder.ITransformProvider) provides the functionality of `AddTransforms` described above as well as DI integration and validation support. - -`ITransformProvider`'s can be registered in DI by calling [AddTransforms<T>()](xref:Microsoft.Extensions.DependencyInjection.ReverseProxyServiceCollectionExtensions). Multiple `ITransformProvider` implementations can be registered and all will be run. - -`ITransformProvider` has two methods, `Validate` and `Apply`. `Validate` gives you the opportunity to inspect the route for any parameters that are needed to configure a transform, such as custom metadata, and to return validation errors on the context if any needed values are missing or invalid. The `Apply` method provides the same functionality as AddTransform as discussed above, adding and configuring transforms per route. - -```C# -services.AddReverseProxy() - .LoadFromConfig(_configuration.GetSection("ReverseProxy")) - .AddTransforms(); -``` -```C# -internal class MyTransformProvider : ITransformProvider -{ - public void ValidateRoute(TransformRouteValidationContext context) - { - // Check all routes for a custom property and validate the associated transform data. - if (context.Route.Metadata?.TryGetValue("CustomMetadata", out var value) ?? false) - { - if (string.IsNullOrEmpty(value)) - { - context.Errors.Add(new ArgumentException("A non-empty CustomMetadata value is required")); - } - } - } - - public void ValidateCluster(TransformClusterValidationContext context) - { - // Check all clusters for a custom property and validate the associated transform data. - if (context.Cluster.Metadata?.TryGetValue("CustomMetadata", out var value) ?? false) - { - if (string.IsNullOrEmpty(value)) - { - context.Errors.Add(new ArgumentException("A non-empty CustomMetadata value is required")); - } - } - } - - public void Apply(TransformBuilderContext transformBuildContext) - { - // Check all routes for a custom property and add the associated transform. - if ((transformBuildContext.Route.Metadata?.TryGetValue("CustomMetadata", out var value) ?? false) - || (transformBuildContext.Cluster?.Metadata?.TryGetValue("CustomMetadata", out value) ?? false)) - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("A non-empty CustomMetadata value is required"); - } - - transformBuildContext.AddRequestTransform(transformContext => - { - transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey("CustomMetadata"), value); - return default; - }); - } - } -} -``` - -### ITransformFactory - -Developers that want to integrate their custom transforms with the `Transforms` section of configuration can implement an [ITransformFactory](xref:Yarp.ReverseProxy.Transforms.Builder.ITransformFactory). This should be registered in DI using the `AddTransformFactory()` method. Multiple factories can be registered and all will be used. - -`ITransformFactory` provides two methods, `Validate` and `Build`. These process one set of transform values at a time, represented by a `IReadOnlyDictionary`. - -The `Validate` method is called when loading a configuration to verify the contents and report all errors. Any reported errors will prevent the configuration from being applied. - -The `Build` method takes the given configuration and produces the associated transform instances for the route. - -```C# -services.AddReverseProxy() - .LoadFromConfig(_configuration.GetSection("ReverseProxy")) - .AddTransformFactory(); -``` -```C# -internal class MyTransformFactory : ITransformFactory -{ - public bool Validate(TransformRouteValidationContext context, IReadOnlyDictionary transformValues) - { - if (transformValues.TryGetValue("CustomTransform", out var value)) - { - if (string.IsNullOrEmpty(value)) - { - context.Errors.Add(new ArgumentException("A non-empty CustomTransform value is required")); - } - - return true; // Matched - } - return false; - } - - public bool Build(TransformBuilderContext context, IReadOnlyDictionary transformValues) - { - if (transformValues.TryGetValue("CustomTransform", out var value)) - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("A non-empty CustomTransform value is required"); - } - - context.AddRequestTransform(transformContext => - { - transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey("CustomTransform"), value); - return default; - }); - - return true; - } - - return false; - } -} -``` - -`Validate` and `Build` return `true` if they've identified the given transform configuration as one that they own. A `ITransformFactory` may implement multiple transforms. Any `RouteConfig.Transforms` entries not handled by any `ITransformFactory` will be considered configuration errors and prevent the configuration from being applied. - -Consider also adding parametrized extension methods on `RouteConfig` like `WithTransformQueryValue` to facilitate programmatic route construction. - -```C# -public static RouteConfig WithTransformQueryValue(this RouteConfig routeConfig, string queryKey, string value, bool append = true) -{ - var type = append ? QueryTransformFactory.AppendKey : QueryTransformFactory.SetKey; - return routeConfig.WithTransform(transform => - { - transform[QueryTransformFactory.QueryValueParameterKey] = queryKey; - transform[type] = value; - }); -} -``` diff --git a/docs/docfx/articles/websockets.md b/docs/docfx/articles/websockets.md deleted file mode 100644 index fb893c424..000000000 --- a/docs/docfx/articles/websockets.md +++ /dev/null @@ -1,27 +0,0 @@ -# Proxing WebSockets and SPDY - -## Introduction - -YARP enables proxying WebSocket and SPDY connections by default. This support works with both the [direct forwarding](direct-forwarding.md) and [full pipeline](getting-started.md) approaches. - -[WebSockets](https://www.rfc-editor.org/rfc/rfc6455.html) is a bidirectional streaming protocol built on HTTP/1.1 and later adapted to [HTTP/2](https://datatracker.ietf.org/doc/html/rfc8441). - -[SPDY](https://www.chromium.org/spdy/spdy-protocol/) is the precursor to HTTP/2 and is commonly used in Kubernetes environments. - -## HTTP/1.1 Upgrades - -WebSockets and SPDY are built on top of HTTP/1.1 using a feature called [connection upgrades](https://datatracker.ietf.org/doc/html/rfc7230#section-6.7). YARP proxies the initial request, and if the destination server responds with `101 Switching Protocols`, upgrades the connection to an opaque, bidirectional stream using the new protocol. YARP does not support upgrading to other protocols like HTTP/2 this way. - -## HTTP/2 - -YARP supports [WebSockets over HTTP/2](https://datatracker.ietf.org/doc/html/rfc8441) starting in .NET 7 and YARP 2.0. Kestrel is the only available AspNetCore server that will accept incoming HTTP/2 WebSocket requests and that support is automatically enabled. Browsers can detect this support advertised by the server and automatically switch to HTTP/2. - -The incoming and outgoing protocol versions do not need to match. The incoming WebSocket request can be HTTP/1.1 or 2. There is no configuration specific to WebSockets for outgoing requests, YARP will use the [ForwarderRequestConfig](xref:Yarp.ReverseProxy.Forwarder.ForwarderRequestConfig)'s Version and VersionPolicy to determine the outbound version to use. These default to HTTP/2 and RequestVersionOrLower. - -WebSockets require different HTTP headers for HTTP/2 so YARP will add and remove these headers as needed when adapting between the different versions. - -After the initial handshake WebSockets function the same way over both HTTP versions. - -## Timeout - -[Http Request Timeouts](https://learn.microsoft.com/aspnet/core/performance/timeouts) (.NET 8+) can apply timeouts to all requests by default or by policy. These timeouts will be disabled after a WebSocket handshake. They will still apply to gRPC requests. For additional configuration see [Timeouts](timeouts.md). diff --git a/docs/docfx/docfx.csproj b/docs/docfx/docfx.csproj deleted file mode 100644 index 2090c4783..000000000 --- a/docs/docfx/docfx.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.0 - Info - - - false - - - - - - \ No newline at end of file diff --git a/docs/docfx/docfx.json b/docs/docfx/docfx.json deleted file mode 100644 index a0badf702..000000000 --- a/docs/docfx/docfx.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "metadata": [ - { - "src": [ - { - "src": "../../src", - "files": [ - "ReverseProxy/Yarp.ReverseProxy.csproj", - "TelemetryConsumption/Yarp.Telemetry.Consumption.csproj" - // "**.csproj" - ] - } - ], - "dest": "api", - "disableGitFeatures": false, - "disableDefaultFilter": false - } - ], - "build": { - "content": [ - { - "files": [ - "api/**.yml", - "api/index.md" - ] - }, - { - "files": [ - "articles/**.md", - "articles/**/toc.yml", - "toc.yml", - "*.md" - ] - } - ], - "resource": [ - { - "files": [ - "images/**" - ] - } - ], - "overwrite": [ - { - "files": [ - "apidoc/**.md" - ], - "exclude": [ - "obj/**", - "_site/**" - ] - } - ], - "dest": "_site", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "template": [ - "default", - "templates/logo_template" - ], - "postProcessors": [], - "markdownEngineName": "markdig", - "noLangKeyword": false, - "keepFileLink": false, - "cleanupCacheHistory": false, - "disableGitFeatures": false, - "xrefService": [ - "https://xref.docs.microsoft.com/query?uid={uid}" - ] - } -} diff --git a/docs/docfx/index.md b/docs/docfx/index.md deleted file mode 100644 index 6d2d87a00..000000000 --- a/docs/docfx/index.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -uid: root -title: YARP Documentation ---- - -# YARP: Yet Another Reverse Proxy - -Welcome to the documentation for YARP! YARP is a library to help create reverse proxy servers that are high-performance, production-ready, and highly customizable. Please provide us your feedback by going to [the GitHub repository](https://github.com/microsoft/reverse-proxy). - -This is the documentation for YARP 2.1. -For documentation of YARP 1.1.1, see https://github.com/microsoft/reverse-proxy/tree/release/1.1/docs/docfx/articles. - -## Why YARP - -We found a bunch of internal teams at Microsoft who were either building a reverse proxy for their service or had been asking about APIs and tech for building one, so we decided to get them all together to work on a common solution, this project. Each of these projects was doing something slightly off the beaten path which meant they were not well served by existing proxies, and customization of those proxies had a high cost and ongoing maintenance considerations. - -Many of the existing proxies were built to support HTTP/1.1, but with workloads changing to include gRPC traffic, they require HTTP/2 support which requires a significantly more complex implementation. By using YARP the projects get to customize the routing and handling behavior without having to implement the http protocol. - -## Using YARP - -YARP is built on .NET using the infrastructure from ASP.NET and .NET (.NET 6 and newer). The key differentiator for YARP is that it's been designed to be easily customized and tweaked via .NET code to match the specific needs of each deployment scenario. - -Eventually we expect YARP to ship as a library, project template, and a single-file exe, to provide a variety of choices for building a robust, performant proxy server. Its pipeline and modules are designed so that you can then customize the functionality for your needs. For example, while YARP supports configuration files, we expect that many users will want to manage the configuration programmatically based on their own configuration management system. YARP provides a configuration API to enable that customization in-proc. - -YARP is designed with customizability as a primary scenario rather than requiring you to break out to script or rebuild the library from source. - -See the [Getting Started](articles/getting-started.md) guide for a brief tutorial, or [Basic Sample](https://github.com/microsoft/reverse-proxy/tree/main/samples/BasicYarpSample) for a fully commented sample showing how to use the YARP library to implement a fairly well featured proxy. diff --git a/docs/docfx/templates/logo_template/logo.svg b/docs/docfx/templates/logo_template/logo.svg deleted file mode 100644 index 5e0441ee2..000000000 --- a/docs/docfx/templates/logo_template/logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/docs/docfx/toc.yml b/docs/docfx/toc.yml deleted file mode 100644 index e269005b3..000000000 --- a/docs/docfx/toc.yml +++ /dev/null @@ -1,8 +0,0 @@ -- name: About YARP 2.1 - href: index.md -- name: Articles - href: articles/ - homepage: articles/index.md -- name: API Documentation - href: api/ - homepage: api/index.md diff --git a/docs/operations/BackportingToPreview.md b/docs/operations/BackportingToPreview.md index 0e6ed4229..c5448ddde 100644 --- a/docs/operations/BackportingToPreview.md +++ b/docs/operations/BackportingToPreview.md @@ -23,12 +23,11 @@ Backporting changes is very similar to a regular release. Changes are made on th Issues with significant security or disclosure concerns need to be fixed privately first. All of this work will happen on the internal Azdo repo and be merged to the public github repo at the time of disclosure. -- Make a separate clone of https://dnceng@dev.azure.com/dnceng/internal/_git/dotnet-yarp to avoid accidentally pushing to the public repo. +- Make a separate clone of https://dev.azure.com/dnceng/internal/_git/dotnet-yarp to avoid accidentally pushing to the public repo. - Create a branch named `internal/release/{version being patched}` starting from the tagged commit of the prior release. - Update versioning as needed. - Create a feature branch, fix the issue, and send a PR using Azdo. -- Once approved and merged, the `internal/release/{version}` branch should build automatically and publish to the `.NET 6 Internal` channel, visible at https://dev.azure.com/dnceng/internal/_packaging?_a=feed&feed=dotnet6-internal. This is configured using the `darc` tool. -- [Release the build](https://github.com/dotnet/yarp/blob/main/docs/operations/Release.md#release-the-build) -- Tag the commit and push it to the public repo +- Release the build from the internal branch. +- Tag the commit and push it to the public repo. Do not push to regular `main` or `release/*` branches in the internal mirror. - Cherry pick the changes to public main as needed. - Finish the standard release checklist. diff --git a/docs/operations/DependencyFlow.md b/docs/operations/DependencyFlow.md index 953223d75..99df4daf7 100644 --- a/docs/operations/DependencyFlow.md +++ b/docs/operations/DependencyFlow.md @@ -1,59 +1,28 @@ -# ***Note that this document may be outdated as .NET 5 has already been released*** - -It is kept here as it may be useful in the future (.NET 6.0) - # Dependency Flow -We are aiming to ship YARP previews aligned with .NET 5, since we are working on changes to the runtime that will improve the experience for YARP. As part of that, our schedule for a preview milestone has three phases: - -1. Open development and receiving new builds of .NET 5 - Commits are going to `main` and new builds of .NET 5 are coming regularly -2. Open development, .NET 5 builds frozen - .NET 5 branches about a month before release, so after they branch we will switch `main` to receive builds from that release branch. Our development will continue until approximately a week before release. -3. Branch and prepare for release - Prior to the release date, we'll branch and prepare for release. Note: in practice we haven't needed to create the release branch until the day before we plan to release. We've also not needed to set up dependency flow in our release branches because the runtime has finished producing new builds that close to release. Main always targets the runtime channel for the next release. - -## Scheduling - -We need to ship versions of our package that depend upon the **released** preview builds. In order to ensure we do that, we try to ship same-date or shortly after .NET 5 previews (see the [.NET Wiki (Internal)](https://aka.ms/dotnet-wiki) for schedule details). - ## Dependency Flow Overview -*For full documentation on Arcade, Maestro and `darc`, see [the Arcade documentation](https://github.com/dotnet/arcade/tree/master/Documentation)* +*For full documentation on Arcade, Maestro and `darc`, see [the Arcade documentation](https://github.com/dotnet/arcade/tree/main/Documentation)* -We use the .NET Engineering System ([Arcade](https://github.com/dotnet/arcade)) to build this repo, since many of the contributors are part of the .NET team and we want to use nightly builds of .NET 5. Part of the engineering system is a service called "Maestro" which manages dependency flow between repositories. When one repository finishes building, it can automatically publish it's build to a Maestro "Channel". Other repos can subscribe to that channel to receive updated builds. Maestro will automatically open a PR to update dependencies in repositories that are subscribed to changes in dependent repositories. +We use the .NET Engineering System ([Arcade](https://github.com/dotnet/arcade)) to build this repo. Part of the engineering system is a service called "Maestro" which manages dependency flow between repositories. When one repository finishes building, it can automatically publish it's build to a Maestro "Channel". Other repos can subscribe to that channel to receive updated builds. Maestro will automatically open a PR to update dependencies in repositories that are subscribed to changes in dependent repositories. Maestro can be queried and controlled using the `darc` command line tool. To use `darc` you will need to be a member of the [`dotnet/arcade-contrib` GitHub Team](https://github.com/orgs/dotnet/teams/arcade-contrib). To set up `darc`: 1. Run `.\eng\common\darc-init.ps1` to install the global tool. -2. Once installed, run `darc authenticate` and follow the instructions in the file opened in your editor to set up the necessary access token for Maestro. You should *only* need the Maestro token for the commands used here, but feel free to configure the other tokens as well. -3. Save and close the file, and `darc` will be ready to go. +2. Once installed, run `darc authenticate` and follow the instructions. Running `darc` with no args will show a list of commands. The `darc help [command]` command will give you help on a specific command. -Repositories can be configured to publish builds automatically to a certain channel, based on the branch. For example, most .NET repos are set up like this: - -* Builds out of `main` are auto-published to the `.NET 5 Dev` channel -* Builds out of `release/5.0.0-preview.X` are auto-published to the `.NET 5 Preview X` channel (where `X` is some preview number) - +Repositories can be configured to publish builds automatically to a certain channel, based on the branch. To see the current mappings for a repository, you can run `darc get-default-channels --source-repo [repo]`, where `[repo]` is any substring that matches a full GitHub URL for a repo in the system. The easiest way to use `[repo]` is to just specify the `[owner]/[name]` form for a repo. For example: ```shell > darc get-default-channels --source-repo dotnet/aspnetcore -(912) https://github.com/dotnet/aspnetcore @ release/3.1 -> .NET Core 3.1 Release -(913) https://github.com/dotnet/aspnetcore @ main -> .NET 5 Dev -(1160) https://github.com/dotnet/aspnetcore @ faster-publishing -> General Testing -(1003) https://github.com/dotnet/aspnetcore @ wtgodbe/NonStablev2 -> General Testing -(1089) https://github.com/dotnet/aspnetcore @ generate-akams-links -> General Testing -(1021) https://github.com/dotnet/aspnetcore @ NonStablePackageVersion -> General Testing -(1018) https://github.com/dotnet/aspnetcore @ wtgodbe/Checksum3x -> General Testing -(1281) https://github.com/dotnet/aspnetcore @ wtgodbe/FixChecksums -> General Testing -(914) https://github.com/dotnet/aspnetcore @ blazor-wasm -> .NET Core 3.1 Blazor Features -(1252) https://github.com/dotnet/aspnetcore @ release/5.0-preview3 -> .NET 5 Preview 3 -(1301) https://github.com/dotnet/aspnetcore @ release/5.0-preview4 -> .NET 5 Preview 4 -(916) https://github.com/dotnet/aspnetcore-tooling @ release/3.1 -> .NET Core 3.1 Release -(917) https://github.com/dotnet/aspnetcore-tooling @ master -> .NET 5 Dev -(1178) https://github.com/dotnet/aspnetcore-tooling @ release/vs16.6-preview2 -> General Testing -(1282) https://github.com/dotnet/aspnetcore-tooling @ wtgodbe/DontPublishDebug -> General Testing -(1253) https://github.com/dotnet/aspnetcore-tooling @ release/5.0-preview3 -> .NET 5 Preview 3 -(1302) https://github.com/dotnet/aspnetcore-tooling @ release/5.0-preview4 -> .NET 5 Preview 4 +(3796) https://github.com/dotnet/aspnetcore @ release/6.0 -> .NET 6 +(5027) https://github.com/dotnet/aspnetcore @ release/8.0 -> .NET 8 +(5731) https://github.com/dotnet/aspnetcore @ release/9.0 -> .NET 9 +(5732) https://github.com/dotnet/aspnetcore @ main -> .NET 10 +(6050) https://github.com/dotnet/aspnetcore @ release/10.0-preview1 -> .NET 10 Preview 1 ``` Subscriptions are managed using the `get-subscriptions`, `add-subscription` and `update-subscription` commands. You can view all subscriptions in the system by running `darc get-subscription`. You can also filter subscriptions by the source and target using the `--source-repo [repo]` and `--target-repo [repo]` arguments. For example, to see everything that `dotnet/yarp` is subscribed to: @@ -61,17 +30,21 @@ Subscriptions are managed using the `get-subscriptions`, `add-subscription` and ```shell > darc get-subscriptions --target-repo dotnet/yarp https://github.com/dotnet/arcade (.NET Eng - Latest) ==> 'https://github.com/dotnet/yarp' ('main') - - Id: 642e03bf-3679-4569-fcfc-08d7d0f045ee + - Id: 1751e896-c0f1-4247-3909-08d8c8762e9e - Update Frequency: EveryWeek - Enabled: True - Batchable: False + - PR Failure Notification tags: + - Source-enabled: False - Merge Policies: Standard -https://github.com/dotnet/runtime (.NET 5 Preview 4) ==> 'https://github.com/dotnet/yarp' ('main') - - Id: 763f49c1-8016-44b6-8810-08d7e1727af8 - - Update Frequency: EveryBuild +https://github.com/dotnet/arcade (.NET Eng - Latest) ==> 'https://github.com/dotnet/yarp' ('release/2.2') + - Id: ebd75d9f-8988-4f50-bd1d-83dfc79fb7ba + - Update Frequency: EveryWeek - Enabled: True - Batchable: False + - PR Failure Notification tags: + - Source-enabled: False - Merge Policies: Standard ``` @@ -91,11 +64,11 @@ Merge Policies: [] A number of comments will also be present, describing available values and what they do. Fill these fields in, for example: ``` -Channel: .NET 5 Dev -Source Repository URL: https://github.com/dotnet/runtime +Channel: .NET Eng - Latest +Source Repository URL: https://github.com/dotnet/arcade Target Repository URL: https://github.com/dotnet/yarp -Target Branch: main -Update Frequency: everyBuild +Target Branch: release/42 +Update Frequency: EveryWeek Batchable: False Merge Policies: - Name: Standard @@ -107,53 +80,26 @@ Similarly, you can edit an existing subscription by using `darc update-subscript ## Prerequisites -* Properly configured `darc` global tool, configured with a Maestro authentication token. - -## When .NET 5 branches for a preview - -When .NET 5 branches for a preview, we need to switch the "channel" from which we are getting builds. See [Dependency Flow Onboarding](https://github.com/dotnet/arcade/blob/master/Documentation/DependencyFlowOnboarding.md) for more information on channels. - -To do this, run the following commands: - -1. Run `darc get-subscriptions --target-repo dotnet/yarp --source-repo dotnet/runtime` to get the subscription from `dotnet/runtime` to `dotnet/yarp` -2. Copy the `Id` value -3. Run `darc update-subscription --id [Id]`. An editor will open. -4. Change the value of the `Channel` field to `.NET 5 Preview X` (where `X` is the preview we are remaining on) -4. Change the value of the `Update Frequency` field to `EveryBuild` (since there should be relatively few builds and we want to be sure we're on the latest). -5. Save and close the file in your editor, `darc` will proceed to make the update. -6. Answer `y` to `Trigger this subscription immediately?` and a PR will be opened to update versions to the latest ones in that channel. -7. Merge the PR as soon as feasible. +* Properly configured `darc` global tool, including having run `darc authenticate`. ## When we are ready to branch Follow the initial branching steps from [Branching.md](Branching.md). -Set up dependency flow so we continue getting new runtime bits if necessary: +YARP uses darc to consume up-to-date Arcade bits every week. +Set up dependency flow for the new branch: 1. Run `darc add-subscription` 2. Fill in the template that opens in your editor as follows: - * `Channel` = `.NET 5 Preview X` where `X` is the .NET 5 preview that matches the YARP preview - * `Source Repository URL` = `https://github.com/dotnet/runtime` + * `Channel` = `.NET Eng - Latest` + * `Source Repository URL` = `https://github.com/dotnet/arcade` * `Target Repository URL` = `https://github.com/dotnet/yarp` - * `Target Branch` = `release/1.0.0-previewX` (where `X` is the YARP preview number; this should be the same as the branch you created above) - * `Update Frequency` = `everyBuild` (Builds will be rare in this channel and we'll want every one) + * `Target Branch` = `release/X` (where `X` is the YARP release version) + * `Update Frequency` = `EveryWeek` * `Merge Policies` is a multiline value, it should look like this: - -``` -Merge Policies: -- Name: Standard - Properties: {} -``` - -3. Save and close the editor window. - -Restore the `main` branch to pulling the latest bits from .NET 5: - -1. Run `darc get-subscriptions --target-repo dotnet/yarp --source-repo dotnet/runtime` to get the subscription from `dotnet/runtime` to `dotnet/yarp` -2. Copy the `Id` value -3. Run `darc update-subscription --id [Id]`. An editor will open. -4. Change the value of the `Channel` field to `.NET 5 Dev` -4. Change the value of the `Update Frequency` field back to `EveryWeek` to reduce PR noise. -5. Save and close the file in your editor, `darc` will proceed to make the update. -6. Answer `y` to `Trigger this subscription immediately?` and a PR will be opened to update versions to the latest ones in that channel. -7. Merge the PR as soon as feasible. \ No newline at end of file + ``` + Merge Policies: + - Name: Standard + Properties: {} + ``` +3. Save and close the editor window. \ No newline at end of file diff --git a/docs/operations/README.md b/docs/operations/README.md index 3d48a3832..4ac6c45bf 100644 --- a/docs/operations/README.md +++ b/docs/operations/README.md @@ -1,10 +1,9 @@ # Operations Playbook -*This documentation is primarily for project maintainers, though contributers are welcome to read and learn about our process!* +*This documentation is primarily for project maintainers, though contributors are welcome to read and learn about our process!* This section has docs on various operations and tasks to be performed in the repo. -* [Writing Status Updates](StatusReport.md) * [Branching](Branching.md) * [Releasing](Release.md) * [Backporting changes onto a preview branch](BackportingToPreview.md) diff --git a/docs/operations/Release.md b/docs/operations/Release.md index 27cf35bd3..c14b07a5d 100644 --- a/docs/operations/Release.md +++ b/docs/operations/Release.md @@ -2,7 +2,7 @@ This document provides a guide on how to release a preview of YARP. -To keep track of the process, open a [release checklist issue](https://github.com/dotnet/yarp/issues/new?title=Preview%20X%20release%20checklist&body=See%20%5BRelease.md%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fblob%2Fmain%2Fdocs%2Foperations%2FRelease.md%29%20for%20detailed%20instructions.%0A%0A-%20%5B%20%5D%20Ensure%20there%27s%20a%20release%20branch%20created%20%28see%20%5BBranching%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fblob%2Fmain%2Fdocs%2Foperations%2FBranching.md%29%29%0A-%20%5B%20%5D%20Ensure%20the%20%60Version.props%60%20has%20the%20%60PreReleaseVersionLabel%60%20updated%20to%20the%20next%20preview%0A-%20%5B%20%5D%20Identify%20and%20validate%20the%20build%20on%20the%20%60microsoft-reverse-proxy-official%60%20pipeline%0A-%20%5B%20%5D%20Release%20the%20build%0A-%20%5B%20%5D%20Tag%20the%20commit%0A-%20%5B%20%5D%20Draft%20release%20notes%0A-%20%5B%20%5D%20Update%20samples%20to%20use%20updated%20package%20versions%0A-%20%5B%20%5D%20Publish%20the%20docs%0A-%20%5B%20%5D%20Publish%20release%20notes%0A-%20%5B%20%5D%20Close%20the%20%5Bold%20milestone%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fmilestones%29%0A-%20%5B%20%5D%20Announce%20on%20social%20media%0A-%20%5B%20%5D%20Set%20the%20preview%20branch%20to%20protected%0A-%20%5B%20%5D%20Delete%20the%20%5Bprevious%20preview%20branch%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fbranches%29%0A-%20%5B%20%5D%20Request%20source%20code%20archival). +To keep track of the process, open a [release checklist issue](https://github.com/dotnet/yarp/issues/new?title=Preview%20X%20release%20checklist&body=See%20%5BRelease.md%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fblob%2Fmain%2Fdocs%2Foperations%2FRelease.md%29%20for%20detailed%20instructions.%0A%0A-%20%5B%20%5D%20Ensure%20there%27s%20a%20release%20branch%20created%20%28see%20%5BBranching%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fblob%2Fmain%2Fdocs%2Foperations%2FBranching.md%29%29%0A-%20%5B%20%5D%20Ensure%20the%20%60Version.props%60%20has%20the%20%60PreReleaseVersionLabel%60%20updated%20to%20the%20next%20preview%0A-%20%5B%20%5D%20Identify%20and%20validate%20the%20build%20on%20the%20%60microsoft-reverse-proxy-official%60%20pipeline%0A-%20%5B%20%5D%20Release%20the%20build%0A-%20%5B%20%5D%20Tag%20the%20commit%0A-%20%5B%20%5D%20Draft%20release%20notes%0A-%20%5B%20%5D%20Publish%20release%20notes%0A-%20%5B%20%5D%20Close%20the%20%5Bold%20milestone%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fmilestones%29%0A-%20%5B%20%5D%20Announce%20on%20social%20media%0A-%20%5B%20%5D%20Set%20the%20preview%20branch%20to%20protected%0A-%20%5B%20%5D%20Delete%20the%20%5Bprevious%20preview%20branch%5D%28https%3A%2F%2Fgithub.com%2Fdotnet%2Fyarp%2Fbranches%29%0A-%20%5B%20%5D%20Request%20source%20code%20archival). ## Versioning @@ -32,6 +32,7 @@ The packages can be accessed from the `PackageArtifacts` artifact: ![image](https://user-images.githubusercontent.com/7574/81447168-fef2bc80-9130-11ea-8aa0-5a83d90efa0d.png) ### Consume .nupkg + - Visual Studio: Place it in a local folder and add that folder as a nuget feed in Visual Studio. - Command Line: `dotnet nuget add source -n local` @@ -87,16 +88,6 @@ Push the tag change to the upstream repo (**not your fork**) Create a draft release at https://github.com/dotnet/yarp/releases using the new tag. See prior releases for the recommended content and format. -## Update samples to use updated package version - -Projects in the **samples** folder use `PackageReference` on the release branch and `ProjectReference` on the main branch. - -Update all project files in the samples folder on the release branch to use `PackageReference` with the newly-released version. Make sure the samples are buildable after this change. - -## Publish the docs - -Reset the `release/latest` branch to the head of the current preview branch to publish the latest docs. See [docs](../docfx/readme.md). - ## Publish the release notes Publish the draft release notes. These should be referencing the latest docs, packages, etc.. diff --git a/eng/Build.props b/eng/Build.props index 24dca905e..82df94d3d 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -6,6 +6,5 @@ - diff --git a/eng/Versions.props b/eng/Versions.props index 1bf716158..2a49d11e4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -31,7 +31,6 @@ 3.0.1 1.1.0 3.1.1 - 2.59.4 16.2.1 15.0.1 From 5af2914f4f6599553686c7400697ef34b3b0c6df Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 14 Feb 2025 05:01:29 +0100 Subject: [PATCH 2/5] Fix contributing file name casing --- contributing.md => CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contributing.md => CONTRIBUTING.md (100%) diff --git a/contributing.md b/CONTRIBUTING.md similarity index 100% rename from contributing.md rename to CONTRIBUTING.md From 6736c40c7780ebbac0e491feaa325c4c32400109 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 14 Feb 2025 05:19:14 +0100 Subject: [PATCH 3/5] Update article doc links --- README.md | 2 +- docs/DailyBuilds.md | 2 +- docs/README.md | 2 +- docs/operations/Release.md | 2 +- samples/KubernetesIngress.Sample/README.md | 20 +++++++++---------- samples/ReverseProxy.Config.Sample/README.md | 8 ++++---- samples/ReverseProxy.Direct.Sample/README.md | 2 +- .../Configuration/HttpClientConfig.cs | 2 +- src/ReverseProxy/README.md | 2 +- src/TelemetryConsumption/README.md | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 5ffcbf848..1cd4a2c86 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We expect YARP to ship as a library and project template that together provide a # Getting started -- See our [Getting Started](https://microsoft.github.io/reverse-proxy/articles/getting-started.html) docs. +- See our [Getting Started](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started) docs. - Try our [previews](https://github.com/dotnet/yarp/releases). - Try our latest [daily build](/docs/DailyBuilds.md). diff --git a/docs/DailyBuilds.md b/docs/DailyBuilds.md index e85c1a829..9f428c35b 100644 --- a/docs/DailyBuilds.md +++ b/docs/DailyBuilds.md @@ -23,7 +23,7 @@ If you want to download the latest daily build and use it in a project, then you *NOTE: This NuGet.Config should be with your application unless you want nightly packages to potentially start being restored for other apps on the machine.* -Then follow the [Getting Started](https://microsoft.github.io/reverse-proxy/articles/getting-started.html) guide to set up a project and add the nuget package dependency. Note daily builds use a higher preview version than given in the docs. +Then follow the [Getting Started](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started) guide to set up a project and add the nuget package dependency. Note daily builds use a higher preview version than given in the docs. Some features, such as new target frameworks, may require prerelease tooling builds for Visual Studio. These are available in the [Visual Studio Preview](https://www.visualstudio.com/vs/preview/). diff --git a/docs/README.md b/docs/README.md index 2d42bff55..b9cdd228f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,4 +5,4 @@ This folder contains: * [Design Notes](designs/) - These are the design notes used to guide our development. They aren't designed to be usage guides but may help contributors in understanding why some patterns were used. * [Operations](operations/) - These are operational docs for running releases and other infrastructure related to the project. -Public documentation is available at [learn.microsoft.com](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/getting-started?view=aspnetcore-9.0). \ No newline at end of file +Public documentation is available at [learn.microsoft.com](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started). \ No newline at end of file diff --git a/docs/operations/Release.md b/docs/operations/Release.md index c14b07a5d..ee97b3137 100644 --- a/docs/operations/Release.md +++ b/docs/operations/Release.md @@ -36,7 +36,7 @@ The packages can be accessed from the `PackageArtifacts` artifact: - Visual Studio: Place it in a local folder and add that folder as a nuget feed in Visual Studio. - Command Line: `dotnet nuget add source -n local` -Walk through the [Getting Started](https://microsoft.github.io/reverse-proxy/articles/getting_started.html) instructions and update them in the release branch as needed. +Walk through the [Getting Started](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started) instructions and update them in the release branch as needed. Also validate any major new scenarios this release and their associated docs. diff --git a/samples/KubernetesIngress.Sample/README.md b/samples/KubernetesIngress.Sample/README.md index bc69b3234..6c660ce1a 100644 --- a/samples/KubernetesIngress.Sample/README.md +++ b/samples/KubernetesIngress.Sample/README.md @@ -106,13 +106,13 @@ The table below lists the available annotations. #### Authorization Policy -See https://microsoft.github.io/reverse-proxy/articles/authn-authz.html for a list of available policies, or how to add your own custom policies. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/authn-authz for a list of available policies, or how to add your own custom policies. `yarp.ingress.kubernetes.io/authorization-policy: anonymous` #### RateLimiter Policy -See https://microsoft.github.io/reverse-proxy/articles/rate-limiting.html for a list of available policies, or how to add your own custom policies. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/rate-limiting for a list of available policies, or how to add your own custom policies. `yarp.ingress.kubernetes.io/rate-limiter-policy: mypolicy` @@ -128,7 +128,7 @@ Specifies the protocol of the backend service. Defaults to http. #### CORS Policy -See https://microsoft.github.io/reverse-proxy/articles/cors.html for the list of available policies, or how to add your own custom policies. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/cors for the list of available policies, or how to add your own custom policies. `yarp.ingress.kubernetes.io/cors-policy: mypolicy` @@ -136,7 +136,7 @@ See https://microsoft.github.io/reverse-proxy/articles/cors.html for the list of Proactively monitors destination health by sending periodic probing requests to designated health endpoints and analyzing responses. -See https://microsoft.github.io/reverse-proxy/articles/dests-health-checks.html. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/dests-health-checks. ``` yarp.ingress.kubernetes.io/health-check | @@ -152,7 +152,7 @@ yarp.ingress.kubernetes.io/health-check | Configures the HTTP client that will be used for the destination service. -See https://microsoft.github.io/reverse-proxy/articles/http-client-config.html. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/http-client-config. ``` yarp.ingress.kubernetes.io/http-client: | @@ -163,7 +163,7 @@ yarp.ingress.kubernetes.io/http-client: | #### Load Balancing -See https://microsoft.github.io/reverse-proxy/articles/load-balancing.html for a list of the available options. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/load-balancing for a list of the available options. `yarp.ingress.kubernetes.io/load-balancing: Random` @@ -179,7 +179,7 @@ yarp.ingress.kubernetes.io/route-metadata: | #### Session Affinity -See https://microsoft.github.io/reverse-proxy/articles/session-affinity.html. +See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/session-affinity. ``` yarp.ingress.kubernetes.io/session-affinity: | @@ -200,7 +200,7 @@ yarp.ingress.kubernetes.io/session-affinity: | #### Transforms -Transforms use the YAML key-value pairs as per the YARP [Request Transforms](https://microsoft.github.io/reverse-proxy/articles/transforms.html#request-transforms) +Transforms use the YAML key-value pairs as per the YARP [Request Transforms](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/transforms#request-transforms) ``` yarp.ingress.kubernetes.io/transforms: | @@ -211,7 +211,7 @@ yarp.ingress.kubernetes.io/transforms: | #### Route Headers -`route-headers` are the YAML representation of YARP [Header Based Routing](https://microsoft.github.io/reverse-proxy/articles/header-routing.html). +`route-headers` are the YAML representation of YARP [Header Based Routing](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/header-routing). See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteHeader.html. @@ -231,7 +231,7 @@ yarp.ingress.kubernetes.io/route-headers: | #### Route QueryParameters -`route-queryparameters` are the YAML representation of YARP [Parameter Based Routing](https://microsoft.github.io/reverse-proxy/articles/queryparameter-routing.html). +`route-queryparameters` are the YAML representation of YARP [Parameter Based Routing](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/queryparameter-routing). See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteQueryParameter.html. diff --git a/samples/ReverseProxy.Config.Sample/README.md b/samples/ReverseProxy.Config.Sample/README.md index 5dd2458b0..53395d0f6 100644 --- a/samples/ReverseProxy.Config.Sample/README.md +++ b/samples/ReverseProxy.Config.Sample/README.md @@ -16,10 +16,10 @@ The configuration shows two routes and two clusters: - A "MyHeader" header will be added with the value "MyValue" - Must have a query parameter "MyQueryParameter" with a value of "value1", "value2" or "another value" - This will route to cluster "allClusterProps" which has 2 destinations - https://dotnet.microsoft.com and https://10.20.30.40 - - Requests will be [load balanced](https://microsoft.github.io/reverse-proxy/articles/load-balancing.html) between destinations using a "PowerOfTwoChoices" algorithm, which picks two destinations at random, then uses the least loaded of the two. - - It includes [session affinity](https://microsoft.github.io/reverse-proxy/articles/session-affinity.html) using a cookie which will ensure subsequent requests from the same client go to the same host. - - It is configured to have both active and passive [health checks](https://microsoft.github.io/reverse-proxy/articles/dests-health-checks.html) - note the second destination will timeout for active checks (unless you have a host with that IP on your network) - - It includes [HttpClient configuration](https://microsoft.github.io/reverse-proxy/articles/http-client-config.html) setting outbound connection properties + - Requests will be [load balanced](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/load-balancing) between destinations using a "PowerOfTwoChoices" algorithm, which picks two destinations at random, then uses the least loaded of the two. + - It includes [session affinity](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/session-affinity) using a cookie which will ensure subsequent requests from the same client go to the same host. + - It is configured to have both active and passive [health checks](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/dests-health-checks) - note the second destination will timeout for active checks (unless you have a host with that IP on your network) + - It includes [HttpClient configuration](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/http-client-config) setting outbound connection properties - HttpRequest properties defaulting to HTTP/2 with a 2min timout The other files in the sample are the same as the getting started instructions. diff --git a/samples/ReverseProxy.Direct.Sample/README.md b/samples/ReverseProxy.Direct.Sample/README.md index a11d5962a..99ab2c7b1 100644 --- a/samples/ReverseProxy.Direct.Sample/README.md +++ b/samples/ReverseProxy.Direct.Sample/README.md @@ -37,7 +37,7 @@ Normal proxying comprises the following steps: To enable control over mapping request and response fields and headers between the client and destination (steps 4 and 7 above), the HttpForwarder.ProxyAsync method takes a HttpTransformer. Your implementation can modify the request url, method, protocol version, response status code, or decide which headers are copied, modify them, or insert additional headers as required. -**Note:** When using the HttpForwarder class directly there are some [header transforms](https://microsoft.github.io/reverse-proxy/articles/transforms.html) included by default, such as adding ```X-Forwarded-For``` and removing the original Host header. This is the same set of transforms included by default in the YARP pipeline model (see BasicYarpSample). +**Note:** When using the HttpForwarder class directly there are some [header transforms](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/transforms) included by default, such as adding ```X-Forwarded-For``` and removing the original Host header. This is the same set of transforms included by default in the YARP pipeline model (see BasicYarpSample). ## Files diff --git a/src/ReverseProxy/Configuration/HttpClientConfig.cs b/src/ReverseProxy/Configuration/HttpClientConfig.cs index 4dfa71d02..4afd2b452 100644 --- a/src/ReverseProxy/Configuration/HttpClientConfig.cs +++ b/src/ReverseProxy/Configuration/HttpClientConfig.cs @@ -14,7 +14,7 @@ namespace Yarp.ReverseProxy.Configuration; /// Options used for communicating with the destination servers. /// /// -/// If you need a more granular approach, please use a custom implementation of . +/// If you need a more granular approach, please use a custom implementation of . /// public sealed record HttpClientConfig { diff --git a/src/ReverseProxy/README.md b/src/ReverseProxy/README.md index 2f51710fd..6417e6f06 100644 --- a/src/ReverseProxy/README.md +++ b/src/ReverseProxy/README.md @@ -1,3 +1,3 @@ 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. -To learn more see the docs at https://microsoft.github.io/reverse-proxy/articles/getting-started.html, 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/. +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/. diff --git a/src/TelemetryConsumption/README.md b/src/TelemetryConsumption/README.md index 384284bea..56b5e91a2 100644 --- a/src/TelemetryConsumption/README.md +++ b/src/TelemetryConsumption/README.md @@ -1,3 +1,3 @@ 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. -To learn more see the docs at https://microsoft.github.io/reverse-proxy/articles/diagnosing-yarp-issues.html#using-telemetry-events and the GitHub repo at https://github.com/dotnet/yarp. +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. From 80c67b320b0bef09fe51d0465c749bd72f4da800 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 14 Feb 2025 05:29:32 +0100 Subject: [PATCH 4/5] Update API reference docs links --- samples/KubernetesIngress.Sample/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/KubernetesIngress.Sample/README.md b/samples/KubernetesIngress.Sample/README.md index 6c660ce1a..8c4de8c68 100644 --- a/samples/KubernetesIngress.Sample/README.md +++ b/samples/KubernetesIngress.Sample/README.md @@ -93,14 +93,14 @@ The table below lists the available annotations. |yarp.ingress.kubernetes.io/output-cache-policy|string| |yarp.ingress.kubernetes.io/backend-protocol|string| |yarp.ingress.kubernetes.io/cors-policy|string| -|yarp.ingress.kubernetes.io/health-check|[ActivateHealthCheckConfig](https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.ActiveHealthCheckConfig.html)| -|yarp.ingress.kubernetes.io/http-client|[HttpClientConfig](https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.HttpClientConfig.html)| +|yarp.ingress.kubernetes.io/health-check|[ActivateHealthCheckConfig](https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.activehealthcheckconfig)| +|yarp.ingress.kubernetes.io/http-client|[HttpClientConfig](https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.httpclientconfig)| |yarp.ingress.kubernetes.io/load-balancing|string| |yarp.ingress.kubernetes.io/route-metadata|Dictionary| -|yarp.ingress.kubernetes.io/session-affinity|[SessionAffinityConfig](https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.SessionAffinityConfig.html)| +|yarp.ingress.kubernetes.io/session-affinity|[SessionAffinityConfig](https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.sessionaffinityconfig)| |yarp.ingress.kubernetes.io/transforms|List>| -|yarp.ingress.kubernetes.io/route-headers|List<[RouteHeader](https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteHeader.html)>| -|yarp.ingress.kubernetes.io/route-queryparameters|List<[RouteQueryParameter](https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteQueryParameter.html)>| +|yarp.ingress.kubernetes.io/route-headers|List<[RouteHeader](https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routeheader)>| +|yarp.ingress.kubernetes.io/route-queryparameters|List<[RouteQueryParameter](https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routequeryparameter)>| |yarp.ingress.kubernetes.io/route-order|int| |yarp.ingress.kubernetes.io/route-methods|List| @@ -169,7 +169,7 @@ See https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/load-balan #### Route Metadata -See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteConfig.html#Yarp_ReverseProxy_Configuration_RouteConfig_Metadata. +See https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routeconfig.metadata#yarp-reverseproxy-configuration-routeconfig-metadata. ``` yarp.ingress.kubernetes.io/route-metadata: | @@ -213,7 +213,7 @@ yarp.ingress.kubernetes.io/transforms: | `route-headers` are the YAML representation of YARP [Header Based Routing](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/header-routing). -See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteHeader.html. +See https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routeheader. ``` yarp.ingress.kubernetes.io/route-headers: | @@ -233,7 +233,7 @@ yarp.ingress.kubernetes.io/route-headers: | `route-queryparameters` are the YAML representation of YARP [Parameter Based Routing](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/queryparameter-routing). -See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteQueryParameter.html. +See https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routequeryparameter. ``` yarp.ingress.kubernetes.io/route-queryparameters: | @@ -251,7 +251,7 @@ yarp.ingress.kubernetes.io/route-queryparameters: | #### Route Order -See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteConfig.html#Yarp_ReverseProxy_Configuration_RouteConfig_Order. +See https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routeconfig.order#yarp-reverseproxy-configuration-routeconfig-order. ``` yarp.ingress.kubernetes.io/route-order: '10' @@ -259,7 +259,7 @@ yarp.ingress.kubernetes.io/route-order: '10' #### Route Methods -See https://microsoft.github.io/reverse-proxy/api/Yarp.ReverseProxy.Configuration.RouteConfig.html#methods. +See https://learn.microsoft.com/dotnet/api/yarp.reverseproxy.configuration.routematch.methods#yarp-reverseproxy-configuration-routematch-methods. ``` yarp.ingress.kubernetes.io/route-methods: | From 5f5603364c01221670b8fb806ba26a3c6fdf6aae Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 14 Feb 2025 05:40:13 +0100 Subject: [PATCH 5/5] Update more links --- docs/designs/config.md | 2 +- docs/designs/yarp-tunneling.md | 2 +- samples/BasicYarpSample/appsettings.json | 2 +- samples/ReverseProxy.Auth.Sample/README.md | 2 +- samples/ReverseProxy.Config.Sample/appsettings.json | 2 +- .../ConfigProvider/ConfigurationReadingExtensions.cs | 2 +- src/ReverseProxy/Configuration/HttpClientConfig.cs | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/designs/config.md b/docs/designs/config.md index 2fbe536ae..4569066f6 100644 --- a/docs/designs/config.md +++ b/docs/designs/config.md @@ -17,7 +17,7 @@ Config based proxies are common and we'll need to support at least basic proxy s We have three relevant components that already have config systems: Kestrel, UrlRewrite, and ReverseProxy. -- [Kestrel](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1#endpoint-configuration) +- [Kestrel](https://learn.microsoft.com/aspnet/core/fundamentals/servers/kestrel/endpoints) - [UrlRewrite](https://github.com/dotnet/aspnetcore/blob/f4d81e3af2b969744a57d76d4d622036ac514a6a/src/Middleware/Rewrite/sample/UrlRewrite.xml#L1-L11) - [ReverseProxy](https://github.com/dotnet/yarp/blob/b2cf5bdddf7962a720672a75f2e93913d16dfee7/samples/IslandGateway.Sample/appsettings.json#L10-L34) diff --git a/docs/designs/yarp-tunneling.md b/docs/designs/yarp-tunneling.md index 41d92a7cc..871730246 100644 --- a/docs/designs/yarp-tunneling.md +++ b/docs/designs/yarp-tunneling.md @@ -6,7 +6,7 @@ ## Introduction While many organizations are moving their computing to the cloud, there are occasions where you need to be able to run some services in a local datacenter. The problem then is if you need to be able to communicate with those services from the cloud. Creating a VPN network connection to Azure or other cloud provider is possible, but usually involves a lot of red tape and configuration complexity as the two networks need to be integrated. -If all that the cloud needs to access is resources that are exposed over http, then a simpler solution is to have a gateway that can route traffic to the remote resource. Additionally, outbound connections over http(s) are not usually blocked, so having a on-prem gateway make an outbound connection to the cloud, is the easiest way to establish the route. This is the basis behind the [Azure Relay](https://docs.microsoft.com/en-us/azure/azure-relay/relay-what-is-it) service offering. +If all that the cloud needs to access is resources that are exposed over http, then a simpler solution is to have a gateway that can route traffic to the remote resource. Additionally, outbound connections over http(s) are not usually blocked, so having a on-prem gateway make an outbound connection to the cloud, is the easiest way to establish the route. This is the basis behind the [Azure Relay](https://learn.microsoft.com/azure/azure-relay/relay-what-is-it) service offering. That is the principle of the tunnel feature for YARP. You operate two instances of the YARP proxy service, configured as a tunnel. The advantage over Azure Relay is that using a reverse proxy as an on-prem gateway means that both cloud and back end services can be used without needing to update the application other than addresses. This is particularly useful for services that may have been written by a 3rd party, or are no longer under active development, and so making changes to the configuration is complicated and expensive. Relay requires the sender and reciever to be updated to use its connection protocol. diff --git a/samples/BasicYarpSample/appsettings.json b/samples/BasicYarpSample/appsettings.json index 39c0a0a9d..b782dab85 100644 --- a/samples/BasicYarpSample/appsettings.json +++ b/samples/BasicYarpSample/appsettings.json @@ -1,6 +1,6 @@ { // Base URLs the server listens on, must be configured independently of the routes below. - // Can also be configured via Kestrel/Endpoints, see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints + // Can also be configured via Kestrel/Endpoints, see https://docs.microsoft.com/aspnet/core/fundamentals/servers/kestrel/endpoints "Urls": "http://localhost:5000;https://localhost:5001", //Sets the Logging level for ASP.NET diff --git a/samples/ReverseProxy.Auth.Sample/README.md b/samples/ReverseProxy.Auth.Sample/README.md index f0711d835..5a957746f 100644 --- a/samples/ReverseProxy.Auth.Sample/README.md +++ b/samples/ReverseProxy.Auth.Sample/README.md @@ -1,6 +1,6 @@ # Authentication & Authorization sample -This sample shows how the YARP proxy can be integrated with the ASP.NET [authentication](https://docs.microsoft.com/aspnet/core/security/authentication) and [authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) system to specify claims requirements on routes that will be enforced by the proxy before it will forward applicable requests. +This sample shows how the YARP proxy can be integrated with the ASP.NET [authentication](https://docs.microsoft.com/aspnet/core/security/authentication) and [authorization](https://docs.microsoft.com/aspnet/core/security/authorization/introduction) system to specify claims requirements on routes that will be enforced by the proxy before it will forward applicable requests. The sample includes the following parts: diff --git a/samples/ReverseProxy.Config.Sample/appsettings.json b/samples/ReverseProxy.Config.Sample/appsettings.json index 30056c07a..1f90e0826 100644 --- a/samples/ReverseProxy.Config.Sample/appsettings.json +++ b/samples/ReverseProxy.Config.Sample/appsettings.json @@ -1,6 +1,6 @@ { // Base URLs the server listens on, must be configured independently of the routes below. - // Can also be configured via Kestrel/Endpoints, see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints + // Can also be configured via Kestrel/Endpoints, see https://docs.microsoft.com/aspnet/core/fundamentals/servers/kestrel/endpoints "Urls": "http://localhost:5000;https://localhost:5001", //Sets the Logging level for ASP.NET diff --git a/src/ReverseProxy/Configuration/ConfigProvider/ConfigurationReadingExtensions.cs b/src/ReverseProxy/Configuration/ConfigProvider/ConfigurationReadingExtensions.cs index ec65948a5..1883eda67 100644 --- a/src/ReverseProxy/Configuration/ConfigProvider/ConfigurationReadingExtensions.cs +++ b/src/ReverseProxy/Configuration/ConfigProvider/ConfigurationReadingExtensions.cs @@ -29,7 +29,7 @@ internal static class ConfigurationReadingExtensions internal static TimeSpan? ReadTimeSpan(this IConfiguration configuration, string name) { // Format "c" => [-][d'.']hh':'mm':'ss['.'fffffff]. - // You also can find more info at https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings#the-constant-c-format-specifier + // You also can find more info at https://docs.microsoft.com/dotnet/standard/base-types/standard-timespan-format-strings#the-constant-c-format-specifier return configuration[name] is string value ? TimeSpan.ParseExact(value, "c", CultureInfo.InvariantCulture) : null; } diff --git a/src/ReverseProxy/Configuration/HttpClientConfig.cs b/src/ReverseProxy/Configuration/HttpClientConfig.cs index 4afd2b452..f70805743 100644 --- a/src/ReverseProxy/Configuration/HttpClientConfig.cs +++ b/src/ReverseProxy/Configuration/HttpClientConfig.cs @@ -62,7 +62,7 @@ public sealed record HttpClientConfig /// Note: If you're using an encoding other than UTF-8 here, then you may also need to configure your server to accept request headers with such an encoding via the corresponding options for the server. /// /// For example, when using Kestrel as the server, use to - /// configure Kestrel to use the same encoding. + /// configure Kestrel to use the same encoding. /// /// public string? RequestHeaderEncoding { get; init; } @@ -78,7 +78,7 @@ public sealed record HttpClientConfig /// Note: If you're using an encoding other than ASCII here, then you may also need to configure your server to send response headers with such an encoding via the corresponding options for the server. /// /// For example, when using Kestrel as the server, use to - /// configure Kestrel to use the same encoding. + /// configure Kestrel to use the same encoding. /// /// public string? ResponseHeaderEncoding { get; init; }