├── .config ├── CredScanSuppressions.json └── tsaoptions.json ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── feedback.md │ └── idea.md ├── policies │ └── resourceManagement.yml └── workflows │ ├── docker_build.yml │ ├── markdownlint-problem-matcher.json │ └── markdownlint.yml ├── .gitignore ├── .markdownlint.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── NuGet.config ├── README.md ├── SECURITY.md ├── TFMs.props ├── THIRD-PARTY-NOTICES.TXT ├── YARP.slnx ├── activate.ps1 ├── activate.sh ├── assets └── icon.png ├── azure-pipelines-pr.yml ├── azure-pipelines.yml ├── build.cmd ├── build.sh ├── docs ├── DailyBuilds.md ├── README.md ├── designs │ ├── README.md │ ├── config.md │ ├── route-extensibility.md │ └── yarp-tunneling.md ├── operations │ ├── BackportingToPreview.md │ ├── Branching.md │ ├── DependencyFlow.md │ ├── README.md │ └── Release.md └── roadmap.md ├── dotnet-yarp-release.yml ├── eng ├── Build.props ├── CodeAnalysis.src.globalconfig ├── CodeAnalysis.test.globalconfig ├── PoliCheckExclusions.xml ├── Publishing.props ├── Signing.props ├── Version.Details.xml ├── Versions.props ├── common │ ├── BuildConfiguration │ │ └── build-configuration.json │ ├── CIBuild.cmd │ ├── PSScriptAnalyzerSettings.psd1 │ ├── README.md │ ├── SetupNugetSources.ps1 │ ├── SetupNugetSources.sh │ ├── build.cmd │ ├── build.ps1 │ ├── build.sh │ ├── cibuild.sh │ ├── core-templates │ │ ├── job │ │ │ ├── job.yml │ │ │ ├── onelocbuild.yml │ │ │ ├── publish-build-assets.yml │ │ │ ├── source-build.yml │ │ │ └── source-index-stage1.yml │ │ ├── jobs │ │ │ ├── codeql-build.yml │ │ │ ├── jobs.yml │ │ │ └── source-build.yml │ │ ├── post-build │ │ │ ├── common-variables.yml │ │ │ ├── post-build.yml │ │ │ └── setup-maestro-vars.yml │ │ ├── steps │ │ │ ├── cleanup-microbuild.yml │ │ │ ├── component-governance.yml │ │ │ ├── enable-internal-runtimes.yml │ │ │ ├── enable-internal-sources.yml │ │ │ ├── generate-sbom.yml │ │ │ ├── get-delegation-sas.yml │ │ │ ├── get-federated-access-token.yml │ │ │ ├── install-microbuild.yml │ │ │ ├── publish-build-artifacts.yml │ │ │ ├── publish-logs.yml │ │ │ ├── publish-pipeline-artifacts.yml │ │ │ ├── retain-build.yml │ │ │ ├── send-to-helix.yml │ │ │ ├── source-build.yml │ │ │ └── source-index-stage1-publish.yml │ │ └── variables │ │ │ └── pool-providers.yml │ ├── cross │ │ ├── armel │ │ │ └── tizen │ │ │ │ └── tizen.patch │ │ ├── build-android-rootfs.sh │ │ ├── build-rootfs.sh │ │ ├── install-debs.py │ │ ├── riscv64 │ │ │ └── tizen │ │ │ │ └── tizen.patch │ │ ├── tizen-build-rootfs.sh │ │ ├── tizen-fetch.sh │ │ └── toolchain.cmake │ ├── darc-init.ps1 │ ├── darc-init.sh │ ├── dotnet-install.cmd │ ├── dotnet-install.ps1 │ ├── dotnet-install.sh │ ├── enable-cross-org-publishing.ps1 │ ├── generate-locproject.ps1 │ ├── generate-sbom-prep.ps1 │ ├── generate-sbom-prep.sh │ ├── helixpublish.proj │ ├── init-tools-native.cmd │ ├── init-tools-native.ps1 │ ├── init-tools-native.sh │ ├── internal-feed-operations.ps1 │ ├── internal-feed-operations.sh │ ├── internal │ │ ├── Directory.Build.props │ │ ├── NuGet.config │ │ └── Tools.csproj │ ├── loc │ │ └── P22DotNetHtmlLocalization.lss │ ├── msbuild.ps1 │ ├── msbuild.sh │ ├── native │ │ ├── CommonLibrary.psm1 │ │ ├── common-library.sh │ │ ├── init-compiler.sh │ │ ├── init-distro-rid.sh │ │ ├── init-os-and-arch.sh │ │ ├── install-cmake-test.sh │ │ ├── install-cmake.sh │ │ ├── install-dependencies.sh │ │ └── install-tool.ps1 │ ├── pipeline-logging-functions.ps1 │ ├── pipeline-logging-functions.sh │ ├── post-build │ │ ├── check-channel-consistency.ps1 │ │ ├── nuget-validation.ps1 │ │ ├── nuget-verification.ps1 │ │ ├── publish-using-darc.ps1 │ │ ├── redact-logs.ps1 │ │ ├── sourcelink-validation.ps1 │ │ └── symbols-validation.ps1 │ ├── retain-build.ps1 │ ├── sdk-task.ps1 │ ├── sdk-task.sh │ ├── sdl │ │ ├── NuGet.config │ │ ├── configure-sdl-tool.ps1 │ │ ├── execute-all-sdl-tools.ps1 │ │ ├── extract-artifact-archives.ps1 │ │ ├── extract-artifact-packages.ps1 │ │ ├── init-sdl.ps1 │ │ ├── packages.config │ │ ├── run-sdl.ps1 │ │ ├── sdl.ps1 │ │ └── trim-assets-version.ps1 │ ├── template-guidance.md │ ├── templates-official │ │ ├── job │ │ │ ├── job.yml │ │ │ ├── onelocbuild.yml │ │ │ ├── publish-build-assets.yml │ │ │ ├── source-build.yml │ │ │ └── source-index-stage1.yml │ │ ├── jobs │ │ │ ├── codeql-build.yml │ │ │ ├── jobs.yml │ │ │ └── source-build.yml │ │ ├── post-build │ │ │ ├── common-variables.yml │ │ │ ├── post-build.yml │ │ │ └── setup-maestro-vars.yml │ │ ├── steps │ │ │ ├── component-governance.yml │ │ │ ├── enable-internal-runtimes.yml │ │ │ ├── enable-internal-sources.yml │ │ │ ├── generate-sbom.yml │ │ │ ├── get-delegation-sas.yml │ │ │ ├── get-federated-access-token.yml │ │ │ ├── publish-build-artifacts.yml │ │ │ ├── publish-logs.yml │ │ │ ├── publish-pipeline-artifacts.yml │ │ │ ├── retain-build.yml │ │ │ ├── send-to-helix.yml │ │ │ ├── source-build.yml │ │ │ └── source-index-stage1-publish.yml │ │ └── variables │ │ │ ├── pool-providers.yml │ │ │ └── sdl-variables.yml │ ├── templates │ │ ├── job │ │ │ ├── job.yml │ │ │ ├── onelocbuild.yml │ │ │ ├── publish-build-assets.yml │ │ │ ├── source-build.yml │ │ │ └── source-index-stage1.yml │ │ ├── jobs │ │ │ ├── codeql-build.yml │ │ │ ├── jobs.yml │ │ │ └── source-build.yml │ │ ├── post-build │ │ │ ├── common-variables.yml │ │ │ ├── post-build.yml │ │ │ └── setup-maestro-vars.yml │ │ ├── steps │ │ │ ├── component-governance.yml │ │ │ ├── enable-internal-runtimes.yml │ │ │ ├── enable-internal-sources.yml │ │ │ ├── generate-sbom.yml │ │ │ ├── get-delegation-sas.yml │ │ │ ├── get-federated-access-token.yml │ │ │ ├── publish-build-artifacts.yml │ │ │ ├── publish-logs.yml │ │ │ ├── publish-pipeline-artifacts.yml │ │ │ ├── retain-build.yml │ │ │ ├── send-to-helix.yml │ │ │ ├── source-build.yml │ │ │ ├── source-index-stage1-publish.yml │ │ │ └── vmr-sync.yml │ │ ├── variables │ │ │ └── pool-providers.yml │ │ └── vmr-build-pr.yml │ ├── tools.ps1 │ ├── tools.sh │ ├── vmr-sync.ps1 │ └── vmr-sync.sh ├── sdl-tsa-vars.config └── yarpapppack │ ├── Common.projitems │ ├── yarpapppack-linux-arm64.csproj │ └── yarpapppack-linux-x64.csproj ├── global.json ├── pack.cmd ├── pack.sh ├── restore.cmd ├── restore.sh ├── samples ├── BasicYarpSample │ ├── BasicYarpSample.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ └── appsettings.json ├── Directory.Build.props ├── KubernetesIngress.Sample │ ├── Combined │ │ ├── Dockerfile │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── README.md │ │ ├── Yarp.Kubernetes.IngressController.csproj │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── ingress-controller.yaml │ ├── Ingress │ │ ├── Dockerfile │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── README.md │ │ ├── Yarp.Kubernetes.Ingress.csproj │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── ingress.yaml │ ├── Monitor │ │ ├── Dockerfile │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── README.md │ │ ├── Yarp.Kubernetes.Monitor.csproj │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── ingress-monitor.yaml │ ├── README.md │ └── backend │ │ ├── Dockerfile │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── README.md │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── backend.csproj │ │ ├── backend.yaml │ │ └── ingress-sample.yaml ├── Prometheus │ ├── HttpLoadApp │ │ ├── HttpLoadApp.csproj │ │ └── Program.cs │ ├── README.md │ ├── ReverseProxy.Metrics-Prometheus.Sample │ │ ├── Program.cs │ │ ├── PrometheusDnsMetrics.cs │ │ ├── PrometheusForwarderMetrics.cs │ │ ├── PrometheusKestrelMetrics.cs │ │ ├── PrometheusOutboundHttpMetrics.cs │ │ ├── PrometheusServiceExtensions.cs │ │ ├── PrometheusSocketMetrics.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── ReverseProxy.Metrics.Prometheus.Sample.csproj │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ ├── graph_screenshot.png │ ├── prometheus.yml │ ├── run10destinations.cmd │ └── run10destinations.sh ├── README.md ├── ReverseProxy.Auth.Sample │ ├── Controllers │ │ └── AccountController.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.Auth.Sample.csproj │ ├── TokenService.cs │ ├── Views │ │ └── Account │ │ │ ├── AccessDenied.cshtml │ │ │ ├── LoggedOut.cshtml │ │ │ └── Login.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ └── favicon.ico ├── ReverseProxy.Code.Sample │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.Code.Sample.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── ReverseProxy.Config.Sample │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.Config.Sample.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── ReverseProxy.ConfigFilter.Sample │ ├── CustomConfigFilter.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.ConfigFilter.Sample.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── ReverseProxy.Direct.Sample │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.Direct.Sample.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── ReverseProxy.HttpSysDelegation.Sample │ ├── README.md │ ├── ReverseProxy │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── ReverseProxy.HttpSysDelegation.Sample.csproj │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── SampleHttpSysServer │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── SampleHttpSysServer.csproj │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── ReverseProxy.LetsEncrypt.Sample │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.LetsEncrypt.Sample.csproj │ └── appsettings.json ├── ReverseProxy.Metrics.Sample │ ├── ForwarderMetricsConsumer.cs │ ├── ForwarderTelemetryConsumer.cs │ ├── HttpClientTelemetryConsumer.cs │ ├── PerRequestMetrics.cs │ ├── PerRequestYarpMetricCollectionMiddleware.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── ReverseProxy.Metrics.Sample.csproj │ ├── WebSocketsTelemetryConsumer.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── ReverseProxy.Transforms.Sample │ ├── MyTransformFactory.cs │ ├── MyTransformProvider.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── ReverseProxy.Transforms.Sample.csproj │ ├── TokenService.cs │ ├── appsettings.Development.json │ └── appsettings.json └── SampleServer │ ├── Controllers │ ├── HealthController.cs │ ├── HttpController.cs │ └── WebSocketsController.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── README.md │ ├── SampleServer.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ └── index.html ├── src ├── Application │ ├── Extensions.cs │ ├── Program.cs │ └── Yarp.Application.csproj ├── Common │ └── Package.targets ├── Directory.Build.props ├── Kubernetes.Controller │ ├── Caching │ │ ├── Endpoints.cs │ │ ├── ICache.cs │ │ ├── IngressCache.cs │ │ ├── IngressClassData.cs │ │ ├── IngressData.cs │ │ ├── NamespaceCache.cs │ │ └── ServiceData.cs │ ├── Certificates │ │ ├── CertificateHelper.cs │ │ ├── ICertificateHelper.cs │ │ ├── IServerCertificateSelector.cs │ │ └── ServerCertificateSelector.cs │ ├── Client │ │ ├── GroupApiVersionKind.cs │ │ ├── IIngressResourceStatusUpdater.cs │ │ ├── IResourceInformer.cs │ │ ├── IResourceInformerRegistration.cs │ │ ├── KubernetesClientOptions.cs │ │ ├── ResourceInformer.cs │ │ ├── ResourceSelector.cs │ │ ├── V1EndpointsResourceInformer.cs │ │ ├── V1IngressClassResourceInformer.cs │ │ ├── V1IngressResourceInformer.cs │ │ ├── V1IngressResourceStatusUpdater.cs │ │ ├── V1SecretResourceInformer.cs │ │ └── V1ServiceResourceInformer.cs │ ├── ConfigProvider │ │ ├── IUpdateConfig.cs │ │ └── KubernetesConfigProvider.cs │ ├── Converters │ │ ├── ClusterTransfer.cs │ │ ├── YarpConfigContext.cs │ │ ├── YarpIngressContext.cs │ │ ├── YarpIngressOptions.cs │ │ └── YarpParser.cs │ ├── Hosting │ │ ├── BackgroundHostedService.cs │ │ ├── HostedServiceAdapter.cs │ │ └── ServiceCollectionHostedServiceAdapterExtensions.cs │ ├── Management │ │ ├── KubernetesCoreExtensions.cs │ │ ├── KubernetesReverseProxyServiceCollectionExtensions.cs │ │ └── KubernetesReverseProxyWebHostBuilderExtensions.cs │ ├── NamespacedName.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Protocol │ │ ├── DispatchActionResult.cs │ │ ├── DispatchConfigProvider.cs │ │ ├── DispatchController.cs │ │ ├── Dispatcher.cs │ │ ├── IDispatchTarget.cs │ │ ├── IDispatcher.cs │ │ ├── Message.cs │ │ ├── MessageConfigProviderExtensions.cs │ │ ├── Receiver.cs │ │ └── ReceiverOptions.cs │ ├── Queues │ │ ├── IWorkQueue.cs │ │ ├── ProcessingRateLimitedQueue.cs │ │ └── WorkQueue.cs │ ├── Rate │ │ ├── Limit.cs │ │ ├── Limiter.cs │ │ └── Reservation.cs │ ├── Services │ │ ├── IReconciler.cs │ │ ├── IngressController.cs │ │ ├── QueueItem.cs │ │ ├── ReconcileData.cs │ │ └── Reconciler.cs │ ├── Yarp.Kubernetes.Controller.csproj │ └── YarpOptions.cs ├── ReverseProxy │ ├── Configuration │ │ ├── ActiveHealthCheckConfig.cs │ │ ├── AuthorizationConstants.cs │ │ ├── ClusterConfig.cs │ │ ├── ClusterValidators │ │ │ ├── DestinationValidator.cs │ │ │ ├── HealthCheckValidator.cs │ │ │ ├── IClusterValidator.cs │ │ │ ├── LoadBalancingValidator.cs │ │ │ ├── ProxyHttpClientValidator.cs │ │ │ ├── ProxyHttpRequestValidator.cs │ │ │ └── SessionAffinityValidator.cs │ │ ├── ConfigProvider │ │ │ ├── ConfigurationConfigProvider.cs │ │ │ ├── ConfigurationReadingExtensions.cs │ │ │ └── ConfigurationSnapshot.cs │ │ ├── ConfigValidator.cs │ │ ├── CorsConstants.cs │ │ ├── DestinationConfig.cs │ │ ├── HeaderMatchMode.cs │ │ ├── HealthCheckConfig.cs │ │ ├── HttpClientConfig.cs │ │ ├── IConfigChangeListener.cs │ │ ├── IConfigValidator.cs │ │ ├── IProxyConfig.cs │ │ ├── IProxyConfigFilter.cs │ │ ├── IProxyConfigProvider.cs │ │ ├── IYarpOutputCachePolicyProvider.cs │ │ ├── IYarpRateLimiterPolicyProvider.cs │ │ ├── InMemoryConfigProvider.cs │ │ ├── InMemoryConfigProviderExtensions.cs │ │ ├── PassiveHealthCheckConfig.cs │ │ ├── QueryParameterMatchMode.cs │ │ ├── RateLimitingConstants.cs │ │ ├── RouteConfig.cs │ │ ├── RouteHeader.cs │ │ ├── RouteMatch.cs │ │ ├── RouteQueryParameter.cs │ │ ├── RouteValidators │ │ │ ├── AuthorizationPolicyValidator.cs │ │ │ ├── CorsPolicyValidator.cs │ │ │ ├── HeadersValidator.cs │ │ │ ├── HostValidator.cs │ │ │ ├── IRouteValidator.cs │ │ │ ├── MethodsValidator.cs │ │ │ ├── OutputCachePolicyValidator.cs │ │ │ ├── PathValidator.cs │ │ │ ├── QueryParametersValidator.cs │ │ │ ├── RateLimitPolicyValidator.cs │ │ │ └── TimeoutPolicyValidator.cs │ │ ├── SessionAffinityConfig.cs │ │ ├── SessionAffinityCookieConfig.cs │ │ ├── TimeoutPolicyConstants.cs │ │ └── WebProxyConfig.cs │ ├── ConfigurationSchema.json │ ├── Delegation │ │ ├── AppBuilderDelegationExtensions.cs │ │ ├── DelegationExtensions.cs │ │ ├── DummyHttpSysDelegator.cs │ │ ├── HttpSysDelegator.cs │ │ ├── HttpSysDelegatorMiddleware.cs │ │ └── IHttpSysDelegator.cs │ ├── Forwarder │ │ ├── CallbackHttpClientFactory.cs │ │ ├── DirectForwardingHttpClientProvider.cs │ │ ├── EmptyHttpContent.cs │ │ ├── ForwarderError.cs │ │ ├── ForwarderErrorFeature.cs │ │ ├── ForwarderHttpClientContext.cs │ │ ├── ForwarderHttpClientFactory.cs │ │ ├── ForwarderMiddleware.cs │ │ ├── ForwarderRequestConfig.cs │ │ ├── ForwarderStage.cs │ │ ├── ForwarderTelemetry.cs │ │ ├── HttpForwarder.cs │ │ ├── HttpTransformer.cs │ │ ├── IForwarderErrorFeature.cs │ │ ├── IForwarderHttpClientFactory.cs │ │ ├── IHttpForwarder.cs │ │ ├── IHttpForwarderExtensions.cs │ │ ├── ProtocolHelper.cs │ │ ├── RequestTransformer.cs │ │ ├── RequestUtilities.cs │ │ ├── ReverseProxyPropagator.cs │ │ ├── StreamCopier.cs │ │ ├── StreamCopyHttpContent.cs │ │ └── StreamCopyResult.cs │ ├── Health │ │ ├── ActiveHealthCheckMonitor.Log.cs │ │ ├── ActiveHealthCheckMonitor.cs │ │ ├── ActiveHealthCheckMonitorOptions.cs │ │ ├── AppBuilderHealthExtensions.cs │ │ ├── ClusterDestinationsUpdater.cs │ │ ├── ConsecutiveFailuresHealthPolicy.cs │ │ ├── ConsecutiveFailuresHealthPolicyOptions.cs │ │ ├── DefaultProbingRequestFactory.cs │ │ ├── DestinationHealthUpdater.cs │ │ ├── DestinationProbingResult.cs │ │ ├── EntityActionScheduler.cs │ │ ├── HealthCheckConstants.cs │ │ ├── HealthyAndUnknownDestinationsPolicy.cs │ │ ├── HealthyOrPanicDestinationsPolicy.cs │ │ ├── IActiveHealthCheckMonitor.cs │ │ ├── IActiveHealthCheckPolicy.cs │ │ ├── IAvailableDestinationsPolicy.cs │ │ ├── IClusterDestinationsUpdater.cs │ │ ├── IDestinationHealthUpdater.cs │ │ ├── IPassiveHealthCheckPolicy.cs │ │ ├── IProbingRequestFactory.cs │ │ ├── NewActiveDestinationHealth.cs │ │ ├── PassiveHealthCheckMiddleware.cs │ │ ├── TransportFailureRateHealthPolicy.cs │ │ └── TransportFailureRateHealthPolicyOptions.cs │ ├── Limits │ │ └── LimitsMiddleware.cs │ ├── LoadBalancing │ │ ├── AppBuilderLoadBalancingExtensions.cs │ │ ├── FirstLoadBalancingPolicy.cs │ │ ├── ILoadBalancingPolicy.cs │ │ ├── LeastRequestsLoadBalancingPolicy.cs │ │ ├── LoadBalancingMiddleware.cs │ │ ├── LoadBalancingPolicies.cs │ │ ├── PowerOfTwoChoicesLoadBalancingPolicy.cs │ │ ├── RandomLoadBalancingPolicy.cs │ │ └── RoundRobinLoadBalancingPolicy.cs │ ├── Management │ │ ├── IProxyStateLookup.cs │ │ ├── IReverseProxyBuilder.cs │ │ ├── IReverseProxyBuilderExtensions.cs │ │ ├── ProxyConfigManager.cs │ │ ├── ReverseProxyBuilder.cs │ │ └── ReverseProxyServiceCollectionExtensions.cs │ ├── Model │ │ ├── ClusterDestinationsState.cs │ │ ├── ClusterModel.cs │ │ ├── ClusterState.cs │ │ ├── DestinationHealth.cs │ │ ├── DestinationHealthState.cs │ │ ├── DestinationModel.cs │ │ ├── DestinationState.cs │ │ ├── HttpContextFeaturesExtensions.cs │ │ ├── IClusterChangeListener.cs │ │ ├── IReverseProxyApplicationBuilder.cs │ │ ├── IReverseProxyFeature.cs │ │ ├── ProxyPipelineInitializerMiddleware.cs │ │ ├── README.md │ │ ├── ReverseProxyApplicationBuilder.cs │ │ ├── ReverseProxyFeature.cs │ │ ├── RouteModel.cs │ │ └── RouteState.cs │ ├── README.md │ ├── Routing │ │ ├── DirectForwardingIEndpointRouteBuilderExtensions.cs │ │ ├── HeaderMatcher.cs │ │ ├── HeaderMatcherPolicy.cs │ │ ├── HeaderMetadata.cs │ │ ├── IHeaderMetadata.cs │ │ ├── IQueryParameterMetadata.cs │ │ ├── ProxyEndpointFactory.cs │ │ ├── QueryParameterMatcher.cs │ │ ├── QueryParameterMatcherPolicy.cs │ │ ├── QueryParameterMetadata.cs │ │ ├── ReverseProxyConventionBuilder.cs │ │ └── ReverseProxyIEndpointRouteBuilderExtensions.cs │ ├── ServiceDiscovery │ │ ├── DnsDestinationResolver.cs │ │ ├── DnsDestinationResolverOptions.cs │ │ ├── IDestinationResolver.cs │ │ ├── NoOpDestinationResolver.cs │ │ └── ResolvedDestinationCollection.cs │ ├── SessionAffinity │ │ ├── AffinitizeTransform.cs │ │ ├── AffinitizeTransformProvider.cs │ │ ├── AffinityHelpers.cs │ │ ├── AffinityResult.cs │ │ ├── AffinityStatus.cs │ │ ├── AppBuilderSessionAffinityExtensions.cs │ │ ├── ArrCookieSessionAffinityPolicy.cs │ │ ├── BaseEncryptedSessionAffinityPolicy.cs │ │ ├── BaseHashCookieSessionAffinityPolicy.cs │ │ ├── CookieSessionAffinityPolicy.cs │ │ ├── CustomHeaderSessionAffinityPolicy.cs │ │ ├── HashCookieSessionAffinityPolicy.cs │ │ ├── IAffinityFailurePolicy.cs │ │ ├── ISessionAffinityPolicy.cs │ │ ├── Log.cs │ │ ├── RedistributeAffinityFailurePolicy.cs │ │ ├── Return503ErrorAffinityFailurePolicy.cs │ │ ├── SessionAffinityConstants.cs │ │ └── SessionAffinityMiddleware.cs │ ├── Transforms │ │ ├── Builder │ │ │ ├── ActionTransformProvider.cs │ │ │ ├── ITransformBuilder.cs │ │ │ ├── ITransformFactory.cs │ │ │ ├── ITransformProvider.cs │ │ │ ├── StructuredTransformer.cs │ │ │ ├── TransformBuilder.cs │ │ │ ├── TransformBuilderContext.cs │ │ │ ├── TransformClusterValidationContext.cs │ │ │ ├── TransformHelpers.cs │ │ │ └── TransformRouteValidationContext.cs │ │ ├── ForwardedTransformActions.cs │ │ ├── ForwardedTransformExtensions.cs │ │ ├── ForwardedTransformFactory.cs │ │ ├── HttpMethodChangeTransform.cs │ │ ├── HttpMethodTransformExtensions.cs │ │ ├── HttpMethodTransformFactory.cs │ │ ├── NodeFormat.cs │ │ ├── PathRouteValuesTransform.cs │ │ ├── PathStringTransform.cs │ │ ├── PathTransformExtensions.cs │ │ ├── PathTransformFactory.cs │ │ ├── QueryParameterFromRouteTransform.cs │ │ ├── QueryParameterFromStaticTransform.cs │ │ ├── QueryParameterRemoveTransform.cs │ │ ├── QueryParameterTransform.cs │ │ ├── QueryTransformContext.cs │ │ ├── QueryTransformExtensions.cs │ │ ├── QueryTransformFactory.cs │ │ ├── RequestFuncTransform.cs │ │ ├── RequestHeaderClientCertTransform.cs │ │ ├── RequestHeaderForwardedTransform.cs │ │ ├── RequestHeaderOriginalHostTransform.cs │ │ ├── RequestHeaderRemoveTransform.cs │ │ ├── RequestHeaderRouteValueTransform.cs │ │ ├── RequestHeaderTransform.cs │ │ ├── RequestHeaderValueTransform.cs │ │ ├── RequestHeaderXForwardedForTransform.cs │ │ ├── RequestHeaderXForwardedHostTransform.cs │ │ ├── RequestHeaderXForwardedPrefixTransform.cs │ │ ├── RequestHeaderXForwardedProtoTransform.cs │ │ ├── RequestHeadersAllowedTransform.cs │ │ ├── RequestHeadersTransformExtensions.cs │ │ ├── RequestHeadersTransformFactory.cs │ │ ├── RequestTransform.cs │ │ ├── RequestTransformContext.cs │ │ ├── ResponseCondition.cs │ │ ├── ResponseFuncTransform.cs │ │ ├── ResponseHeaderRemoveTransform.cs │ │ ├── ResponseHeaderValueTransform.cs │ │ ├── ResponseHeadersAllowedTransform.cs │ │ ├── ResponseTrailerRemoveTransform.cs │ │ ├── ResponseTrailerValueTransform.cs │ │ ├── ResponseTrailersAllowedTransform.cs │ │ ├── ResponseTrailersFuncTransform.cs │ │ ├── ResponseTrailersTransform.cs │ │ ├── ResponseTrailersTransformContext.cs │ │ ├── ResponseTransform.cs │ │ ├── ResponseTransformContext.cs │ │ ├── ResponseTransformExtensions.cs │ │ ├── ResponseTransformFactory.cs │ │ ├── RouteConfigTransformExtensions.cs │ │ └── TransformBuilderContextFuncExtensions.cs │ ├── Utilities │ │ ├── ActivityCancellationTokenSource.cs │ │ ├── AtomicCounter.cs │ │ ├── CaseInsensitiveEqualHelper.cs │ │ ├── CaseSensitiveEqualHelper.cs │ │ ├── CollectionEqualityHelper.cs │ │ ├── ConcurrentDictionaryExtensions.cs │ │ ├── DelegatingStream.cs │ │ ├── EventIds.cs │ │ ├── IClock.cs │ │ ├── IRandomFactory.cs │ │ ├── NullRandomFactory.cs │ │ ├── Observability.cs │ │ ├── ParsedMetadataEntry.cs │ │ ├── RandomFactory.cs │ │ ├── ServiceLookupHelper.cs │ │ ├── SkipLocalsInit.cs │ │ ├── TaskUtilities.cs │ │ ├── TlsFrameHelper.cs │ │ ├── ValueStopwatch.cs │ │ └── ValueStringBuilder.cs │ ├── WebSocketsTelemetry │ │ ├── HttpConnectFeatureWrapper.cs │ │ ├── HttpUpgradeFeatureWrapper.cs │ │ ├── WebSocketCloseReason.cs │ │ ├── WebSocketsParser.cs │ │ ├── WebSocketsTelemetry.cs │ │ ├── WebSocketsTelemetryExtensions.cs │ │ ├── WebSocketsTelemetryMiddleware.cs │ │ └── WebSocketsTelemetryStream.cs │ └── Yarp.ReverseProxy.csproj └── TelemetryConsumption │ ├── EventListenerService.cs │ ├── Forwarder │ ├── ForwarderEventListenerService.cs │ ├── ForwarderMetrics.cs │ ├── ForwarderStage.cs │ └── IForwarderTelemetryConsumer.cs │ ├── Http │ ├── HttpEventListenerService.cs │ ├── HttpMetrics.cs │ └── IHttpTelemetryConsumer.cs │ ├── IMetricsConsumer.cs │ ├── Kestrel │ ├── IKestrelTelemetryConsumer.cs │ ├── KestrelEventListenerService.cs │ └── KestrelMetrics.cs │ ├── MetricsOptions.cs │ ├── NameResolution │ ├── INameResolutionTelemetryConsumer.cs │ ├── NameResolutionEventListenerService.cs │ └── NameResolutionMetrics.cs │ ├── NetSecurity │ ├── INetSecurityTelemetryConsumer.cs │ ├── NetSecurityEventListenerService.cs │ └── NetSecurityMetrics.cs │ ├── README.md │ ├── Sockets │ ├── ISocketsTelemetryConsumer.cs │ ├── SocketsEventListenerService.cs │ └── SocketsMetrics.cs │ ├── TelemetryConsumptionExtensions.cs │ ├── WebSockets │ ├── IWebSocketsTelemetryConsumer.cs │ ├── WebSocketCloseReason.cs │ └── WebSocketsEventListenerService.cs │ └── Yarp.Telemetry.Consumption.csproj ├── startvs.cmd ├── test.cmd ├── test.sh ├── test ├── Directory.Build.props ├── Kubernetes.Tests │ ├── Certificates │ │ ├── CertificateHelperTests.cs │ │ ├── cert.der │ │ ├── cert.pem │ │ ├── key.der │ │ └── key.pem │ ├── Client │ │ ├── ResourceInformerTests.cs │ │ ├── SyncResourceInformer.cs │ │ ├── V1DeploymentResourceInformer.cs │ │ └── V1PodResourceInformer.cs │ ├── Hosting │ │ ├── BackgroundHostedServiceTests.cs │ │ └── Fakes │ │ │ ├── FakeBackgroundHostedService.cs │ │ │ ├── FakeServer.cs │ │ │ ├── TestLatch.cs │ │ │ └── TestLatches.cs │ ├── IngressCacheTests.cs │ ├── IngressControllerTests.cs │ ├── IngressConversionTests.cs │ ├── KubeResourceGenerator.cs │ ├── Management │ │ └── KubernetesCoreExtensionsTests.cs │ ├── NamespacedNameTests.cs │ ├── Queues │ │ └── WorkQueueTests.cs │ ├── Rate │ │ ├── LimitTests.cs │ │ ├── LimiterTests.cs │ │ └── ReservationTests.cs │ ├── ReconcilerTests.cs │ ├── TestCluster │ │ ├── Controllers │ │ │ ├── ResourceApiController.cs │ │ │ └── ResourceApiGroupController.cs │ │ ├── ITestCluster.cs │ │ ├── ITestClusterHost.cs │ │ ├── Models │ │ │ ├── ListParameters.cs │ │ │ ├── ListResult.cs │ │ │ └── ResourceObject.cs │ │ ├── TestCluster.cs │ │ ├── TestClusterHost.cs │ │ ├── TestClusterHostBuilder.cs │ │ ├── TestClusterOptions.cs │ │ └── TestClusterStartup.cs │ ├── Utils │ │ ├── ResourceSerializers.cs │ │ └── TestLogger.cs │ ├── Yarp.Kubernetes.Tests.csproj │ └── testassets │ │ ├── annotations │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── basic-ingress │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── exact-match │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── external-name-ingress │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── hostname-routing │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── https-service-port-protocol │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── https │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── ingress-class-not-set │ │ └── ingress.yaml │ │ ├── ingress-class-set │ │ └── ingress.yaml │ │ ├── mapped-port │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── missing-svc │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-endpoints-ports │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-endpoints-same-port │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-hosts │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-ingresses-one-svc │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-ingresses │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── multiple-namespaces │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── port-diff-name │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── port-mismatch │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── resource-informer │ │ ├── ResourcesAreListedWhenReadyAsyncIsComplete │ │ │ ├── resources.yaml │ │ │ └── shouldbe.yaml │ │ └── ResourcesWithApiGroupAreListed │ │ │ ├── resources.yaml │ │ │ └── shouldbe.yaml │ │ ├── route-headers │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── route-metadata │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── route-methods │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ ├── route-order │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json │ │ └── route-queryparameters │ │ ├── clusters.json │ │ ├── ingress.yaml │ │ └── routes.json ├── ReverseProxy.FunctionalTests │ ├── Common │ │ ├── Helpers.cs │ │ ├── HttpSysTestEnvironment.cs │ │ ├── TestEnvironment.cs │ │ └── TestUrlHelper.cs │ ├── DistributedTracingTests.cs │ ├── Expect100ContinueTests.cs │ ├── HeaderTests.cs │ ├── HttpForwarderCancellationTests.cs │ ├── HttpProxyCookieTests.cs │ ├── HttpSysDelegationTests.cs │ ├── PassiveHealthCheckTests.cs │ ├── TelemetryConsumptionTests.cs │ ├── TelemetryEnumTests.cs │ ├── WebSocketTests.cs │ ├── WebSocketsTelemetryTests.cs │ └── Yarp.ReverseProxy.FunctionalTests.csproj ├── ReverseProxy.Tests │ ├── Common │ │ ├── EventAssertExtensions.cs │ │ ├── HttpContentExtensions.cs │ │ ├── MockHttpHandler.cs │ │ ├── TaskExtensions.cs │ │ ├── TestEventListener.cs │ │ ├── TestResources.cs │ │ └── TestTrailersFeature.cs │ ├── Configuration │ │ ├── ActiveHealthCheckConfigTests.cs │ │ ├── ClusterConfigTests.cs │ │ ├── ConfigProvider │ │ │ ├── ConfigurationConfigProviderTests.cs │ │ │ └── ConfigurationReadingExtensionsTests.cs │ │ ├── ConfigValidatorTests.cs │ │ ├── DestinationConfigTests.cs │ │ ├── HealthCheckConfigTests.cs │ │ ├── HttpClientConfigTests.cs │ │ ├── PassiveHealthCheckConfigTests.cs │ │ ├── RouteConfigTests.cs │ │ ├── RouteHeaderTests.cs │ │ ├── RouteMatchTests.cs │ │ ├── RouteQueryParameterTests.cs │ │ ├── SessionAffinityConfigTests.cs │ │ ├── YarpOutputCachePolicyProviderTests.cs │ │ └── YarpRateLimiterPolicyProviderTests.cs │ ├── Delegation │ │ ├── HttpSysDelegatorMiddlewareTests.cs │ │ └── HttpSysDelegatorTests.cs │ ├── Forwarder │ │ ├── ForwarderHttpClientFactoryTests.cs │ │ ├── ForwarderMiddlewareTests.cs │ │ ├── HttpForwarderTests.cs │ │ ├── HttpTransformerTests.cs │ │ ├── RequestUtilitiesTests.cs │ │ ├── ReverseProxyServiceCollectionTests.cs │ │ ├── StreamCopierTests.cs │ │ └── StreamCopyHttpContentTests.cs │ ├── Health │ │ ├── ActiveHealthCheckMonitorTests.cs │ │ ├── ClusterDestinationsUpdaterTests.cs │ │ ├── ConsecutiveFailuresHealthPolicyTests.cs │ │ ├── DefaultProbingRequestFactoryTests.cs │ │ ├── DestinationHealthUpdaterTests.cs │ │ ├── EntityActionSchedulerTests.cs │ │ ├── HealthyAndUnknownDestinationsPolicyTests.cs │ │ ├── HealthyOrPanicDestinationsPolicyTests.cs │ │ ├── PassiveHealthCheckMiddlewareTests.cs │ │ └── TransportFailureRateHealthPolicyTests.cs │ ├── Limits │ │ └── LimitsMiddlewareTests.cs │ ├── LoadBalancing │ │ ├── LoadBalancerMiddlewareTests.cs │ │ └── LoadBalancingPoliciesTests.cs │ ├── Management │ │ └── ProxyConfigManagerTests.cs │ ├── Model │ │ ├── DestinationStateTests.cs │ │ ├── HttpContextFeaturesExtensions.cs │ │ └── ProxyPipelineInitializerMiddlewareTests.cs │ ├── Routing │ │ ├── HeaderMatcherPolicyTests.cs │ │ ├── ProxyEndpointFactoryTests.cs │ │ ├── QueryMatcherPolicyTests.cs │ │ ├── ReverseProxyConventionBuilderTests.cs │ │ └── RoutingTests.cs │ ├── SessionAffinity │ │ ├── AffinitizeTransformProviderTests.cs │ │ ├── AffinitizeTransformTests.cs │ │ ├── AffinityTestHelper.cs │ │ ├── ArrCookieSessionAffinityPolicyTests.cs │ │ ├── BaseSessionAffinityPolicyTests.cs │ │ ├── CookieSessionAffinityPolicyTests.cs │ │ ├── CustomHeaderSessionAffinityPolicyTests.cs │ │ ├── HashCookieSessionAffinityPolicyTests.cs │ │ ├── RedistributeAffinityFailurePolicyTests.cs │ │ ├── Return503ErrorAffinityFailurePolicyTests.cs │ │ └── SessionAffinityMiddlewareTests.cs │ ├── Transforms │ │ ├── Builder │ │ │ └── TransformBuilderTests.cs │ │ ├── DestinationPrefixTransformTests.cs │ │ ├── ForwardedTransformExtensionsTests.cs │ │ ├── HttpMethodChangeTransformTests.cs │ │ ├── HttpMethodTransformExtensionsTests.cs │ │ ├── PathRouteValuesTransformTests.cs │ │ ├── PathStringTransformTests.cs │ │ ├── PathTransformExtensionsTests.cs │ │ ├── QueryParameterFromRouteTransformTests.cs │ │ ├── QueryParameterFromStaticTransformTests.cs │ │ ├── QueryParameterRemoveTransformTests.cs │ │ ├── QueryTransformContextTests.cs │ │ ├── QueryTransformExtensionsTests.cs │ │ ├── RequestHeaderClientCertTransformTests.cs │ │ ├── RequestHeaderForwardedTransformTests.cs │ │ ├── RequestHeaderRemoveTransformTests.cs │ │ ├── RequestHeaderRouteValueTransformTests.cs │ │ ├── RequestHeaderValueTransformTests.cs │ │ ├── RequestHeaderXForwardedForTransformTests.cs │ │ ├── RequestHeaderXForwardedHostTransformTests.cs │ │ ├── RequestHeaderXForwardedPrefixTransformTests.cs │ │ ├── RequestHeaderXForwardedProtoTransformTests.cs │ │ ├── RequestHeadersAllowedTransformTests.cs │ │ ├── RequestHeadersTransformExtensionsTests.cs │ │ ├── RequestTransformTests.cs │ │ ├── ResponseHeaderRemoveTransformTests.cs │ │ ├── ResponseHeaderValueTransformTests.cs │ │ ├── ResponseHeadersAllowedTransformTests.cs │ │ ├── ResponseTrailerRemoveTransformTests.cs │ │ ├── ResponseTrailerValueTransformTests.cs │ │ ├── ResponseTrailersAllowedTransformTests.cs │ │ ├── ResponseTrailersTransformTests.cs │ │ ├── ResponseTransformExtensionsTests.cs │ │ ├── ResponseTransformTests.cs │ │ ├── TransformBuilderContextFuncExtensionsTests.cs │ │ └── TransformExtensionsTestsBase.cs │ ├── Utilities │ │ ├── ActivityCancellationTokenSourceTests.cs │ │ ├── AtomicCounterTests.cs │ │ ├── CaseInsensitiveEqualHelperTests.cs │ │ ├── RandomFactoryTests.cs │ │ └── TlsFrameHelperTests.cs │ ├── WebSocketsTelemetry │ │ └── WebSocketsParserTests.cs │ ├── Yarp.ReverseProxy.Tests.csproj │ └── validSelfSignedClientEkuCertificate.cer ├── TestCertificates │ └── testCert.pfx └── Tests.Common │ ├── TestAutoMockBase.cs │ ├── TestLogger.cs │ ├── TestLoggerProvider.cs │ ├── TestRandom.cs │ ├── TestRandomFactory.cs │ ├── TestTimeProvider.cs │ ├── XunitLoggerFactoryExtensions.cs │ ├── XunitLoggerProvider.cs │ └── Yarp.Tests.Common.csproj └── testassets ├── BenchmarkApp ├── BenchmarkApp.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── README.md ├── appsettings.json └── testCert.pfx ├── Directory.Build.props ├── ReverseProxy.Code ├── Controllers │ └── HealthController.cs ├── ForwarderMetricsConsumer.cs ├── ForwarderTelemetryConsumer.cs ├── MyTransformFactory.cs ├── MyTransformProvider.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── ReverseProxy.Code.csproj ├── TokenService.cs ├── appsettings.Development.json └── appsettings.json ├── ReverseProxy.Config ├── Controllers │ └── HealthController.cs ├── CustomConfigFilter.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── ReverseProxy.Config.csproj ├── appsettings.Development.json └── appsettings.json ├── ReverseProxy.Direct ├── Program.cs ├── Properties │ └── launchSettings.json ├── ReverseProxy.Direct.csproj ├── TlsFilter.cs ├── appsettings.Development.json └── appsettings.json ├── TestClient ├── CommandLineArgs.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Scenarios │ ├── Http1Scenario.cs │ ├── Http2PostExpectContinueScenario.cs │ ├── Http2Scenario.cs │ ├── IScenario.cs │ ├── RawUpgradeScenario.cs │ ├── SessionAffinityScenario.cs │ └── WebSocketsScenario.cs └── TestClient.csproj └── TestServer ├── AssemblyInfo.cs ├── Controllers ├── HealthController.cs ├── HttpController.cs ├── UpgradeController.cs └── WebSocketsController.cs ├── Program.cs ├── Properties └── launchSettings.json ├── TestServer.csproj ├── appsettings.Development.json └── appsettings.json /.config/CredScanSuppressions.json: -------------------------------------------------------------------------------- 1 | { 2 | "tool": "Credential Scanner", 3 | "suppressions": [ 4 | { 5 | "_justification": "Legitimate key/cert used for testing", 6 | "file": [ 7 | "testassets/BenchmarkApp/testCert.pfx", 8 | "test/TestCertificates/testCert.pfx", 9 | "test/Kubernetes.Tests/Certificates/key.der", 10 | "test/Kubernetes.Tests/Certificates/key.pem" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "areaPath": "DevDiv\\ASP.NET Core\\YARP", 3 | "codebaseName": "ReverseProxy", 4 | "instanceUrl": "https://devdiv.visualstudio.com/", 5 | "iterationPath": "DevDiv", 6 | "notificationAliases": [ 7 | "dotnetrp@microsoft.com" 8 | ], 9 | "projectName": "DEVDIV", 10 | "repositoryName": "yarp", 11 | "template": "TFSDEVDIV" 12 | } 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md 26 | **/ingress.yaml 27 | **/ingress-controller.yaml 28 | **/backend.yaml 29 | **/ingress-sample.yaml 30 | .dotnet 31 | TestResults 32 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths. 2 | # See https://help.github.com/articles/about-code-owners/ 3 | 4 | * @MihaZupan @benjaminpetit 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report about something that is not working 4 | labels: "Type: Bug" 5 | --- 6 | 7 | ### Describe the bug 8 | A clear and concise description of what the bug is. 9 | 10 | ### To Reproduce 11 | 17 | 18 | ### Further technical details 19 | 20 | - Include the version of the packages you are using 21 | - The platform (Linux/macOS/Windows) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feedback 3 | about: Tell us what you think! 4 | labels: "Type: Feedback" 5 | --- 6 | 7 | ### Some details 8 | 9 | **How many backends are in your application?** 10 | 11 | - [ ] 1-2 12 | - [ ] 3-5 13 | - [ ] 6-10 14 | - [ ] 10+ 15 | 16 | **How do you host your application?** 17 | 18 | - [ ] Kubernetes 19 | - [ ] Azure App Service 20 | - [ ] Azure VMs 21 | - [ ] Other Cloud Provider (please include details below) 22 | - [ ] Other Hosting model (please include details below) 23 | 24 | ### What did you think of YARP? -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/idea.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Idea 3 | about: Ideas, feature requests, and wishes 4 | labels: "Type: Idea" 5 | --- 6 | 7 | ### What should we add or change to make your life better? 8 | 9 | ### Why is this important to you? -------------------------------------------------------------------------------- /.github/workflows/markdownlint-problem-matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "markdownlint", 5 | "pattern": [ 6 | { 7 | "regexp": "^([^:]*):(\\d+):?(\\d+)?\\s([\\w-\\/]*)\\s(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "code": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/markdownlint.yml: -------------------------------------------------------------------------------- 1 | name: Markdownlint 2 | 3 | permissions: 4 | contents: read 5 | 6 | # run even on changes without markdown changes, so that we can 7 | # make it in GitHub a required check for PR's 8 | on: 9 | pull_request: 10 | 11 | jobs: 12 | lint: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Use Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: 16.x 22 | - name: Run Markdownlint 23 | run: | 24 | echo "::add-matcher::.github/workflows/markdownlint-problem-matcher.json" 25 | npm i -g markdownlint-cli 26 | markdownlint "**.md" 27 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. 4 | For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TFMs.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | net8.0 6 | net8.0;net9.0 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/assets/icon.png -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -restore -build %*" 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source="${BASH_SOURCE[0]}" 4 | 5 | # resolve $SOURCE until the file is no longer a symlink 6 | while [[ -h $source ]]; do 7 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 8 | source="$(readlink "$source")" 9 | 10 | # if $source was a relative symlink, we need to resolve it relative to the path where the 11 | # symlink file was located 12 | [[ $source != /* ]] && source="$scriptroot/$source" 13 | done 14 | 15 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 16 | "$scriptroot/eng/common/build.sh" --build --restore $@ 17 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docs Folder 2 | 3 | This folder contains: 4 | 5 | * [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. 6 | * [Operations](operations/) - These are operational docs for running releases and other infrastructure related to the project. 7 | 8 | Public documentation is available at [learn.microsoft.com](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/getting-started). -------------------------------------------------------------------------------- /docs/designs/README.md: -------------------------------------------------------------------------------- 1 | # Design Meeting Notes 2 | 3 | Rough meeting notes from design meetings. -------------------------------------------------------------------------------- /docs/operations/README.md: -------------------------------------------------------------------------------- 1 | # Operations Playbook 2 | 3 | *This documentation is primarily for project maintainers, though contributors are welcome to read and learn about our process!* 4 | 5 | This section has docs on various operations and tasks to be performed in the repo. 6 | 7 | * [Branching](Branching.md) 8 | * [Releasing](Release.md) 9 | * [Backporting changes onto a preview branch](BackportingToPreview.md) 10 | * [Dependency flow](DependencyFlow.md) -------------------------------------------------------------------------------- /eng/Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /eng/PoliCheckExclusions.xml: -------------------------------------------------------------------------------- 1 | 2 | SWAGGER.JSON 3 | 4 | -------------------------------------------------------------------------------- /eng/Version.Details.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | https://github.com/dotnet/arcade 8 | aba421eb78276b26d1a24df7772474806b27aa13 9 | 10 | 11 | https://github.com/dotnet/arcade 12 | aba421eb78276b26d1a24df7772474806b27aa13 13 | 14 | 15 | https://github.com/dotnet/arcade 16 | aba421eb78276b26d1a24df7772474806b27aa13 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /eng/common/BuildConfiguration/build-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "RetryCountLimit": 1, 3 | "RetryByAnyError": false 4 | } 5 | -------------------------------------------------------------------------------- /eng/common/CIBuild.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*" 3 | -------------------------------------------------------------------------------- /eng/common/PSScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | IncludeRules=@('PSAvoidUsingCmdletAliases', 3 | 'PSAvoidUsingWMICmdlet', 4 | 'PSAvoidUsingPositionalParameters', 5 | 'PSAvoidUsingInvokeExpression', 6 | 'PSUseDeclaredVarsMoreThanAssignments', 7 | 'PSUseCmdletCorrectly', 8 | 'PSStandardDSCFunctionsInResource', 9 | 'PSUseIdenticalMandatoryParametersForDSC', 10 | 'PSUseIdenticalParametersForDSC') 11 | } -------------------------------------------------------------------------------- /eng/common/build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" 3 | exit /b %ErrorLevel% 4 | -------------------------------------------------------------------------------- /eng/common/cibuild.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source="${BASH_SOURCE[0]}" 4 | 5 | # resolve $SOURCE until the file is no longer a symlink 6 | while [[ -h $source ]]; do 7 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 8 | source="$(readlink "$source")" 9 | 10 | # if $source was a relative symlink, we need to resolve it relative to the path where 11 | # the symlink file was located 12 | [[ $source != /* ]] && source="$scriptroot/$source" 13 | done 14 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 15 | 16 | . "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@ 17 | -------------------------------------------------------------------------------- /eng/common/core-templates/post-build/common-variables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - group: Publish-Build-Assets 3 | 4 | # Whether the build is internal or not 5 | - name: IsInternalBuild 6 | value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} 7 | 8 | # Default Maestro++ API Endpoint and API Version 9 | - name: MaestroApiEndPoint 10 | value: "https://maestro.dot.net" 11 | - name: MaestroApiVersion 12 | value: "2020-02-20" 13 | 14 | - name: SourceLinkCLIVersion 15 | value: 3.0.0 16 | - name: SymbolToolVersion 17 | value: 1.0.1 18 | - name: BinlogToolVersion 19 | value: 1.0.11 20 | 21 | - name: runCodesignValidationInjection 22 | value: false 23 | -------------------------------------------------------------------------------- /eng/common/core-templates/steps/component-governance.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | disableComponentGovernance: false 3 | componentGovernanceIgnoreDirectories: '' 4 | is1ESPipeline: false 5 | displayName: 'Component Detection' 6 | 7 | steps: 8 | - ${{ if eq(parameters.disableComponentGovernance, 'true') }}: 9 | - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" 10 | displayName: Set skipComponentGovernanceDetection variable 11 | - ${{ if ne(parameters.disableComponentGovernance, 'true') }}: 12 | - task: ComponentGovernanceComponentDetection@0 13 | continueOnError: true 14 | displayName: ${{ parameters.displayName }} 15 | inputs: 16 | ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} 17 | -------------------------------------------------------------------------------- /eng/common/core-templates/steps/publish-build-artifacts.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: is1ESPipeline 3 | type: boolean 4 | default: false 5 | - name: args 6 | type: object 7 | default: {} 8 | steps: 9 | - ${{ if ne(parameters.is1ESPipeline, true) }}: 10 | - template: /eng/common/templates/steps/publish-build-artifacts.yml 11 | parameters: 12 | is1ESPipeline: ${{ parameters.is1ESPipeline }} 13 | ${{ each parameter in parameters.args }}: 14 | ${{ parameter.key }}: ${{ parameter.value }} 15 | - ${{ else }}: 16 | - template: /eng/common/templates-official/steps/publish-build-artifacts.yml 17 | parameters: 18 | is1ESPipeline: ${{ parameters.is1ESPipeline }} 19 | ${{ each parameter in parameters.args }}: 20 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/core-templates/steps/publish-pipeline-artifacts.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: is1ESPipeline 3 | type: boolean 4 | default: false 5 | 6 | - name: args 7 | type: object 8 | default: {} 9 | 10 | steps: 11 | - ${{ if ne(parameters.is1ESPipeline, true) }}: 12 | - template: /eng/common/templates/steps/publish-pipeline-artifacts.yml 13 | parameters: 14 | ${{ each parameter in parameters }}: 15 | ${{ parameter.key }}: ${{ parameter.value }} 16 | - ${{ else }}: 17 | - template: /eng/common/templates-official/steps/publish-pipeline-artifacts.yml 18 | parameters: 19 | ${{ each parameter in parameters }}: 20 | ${{ parameter.key }}: ${{ parameter.value }} 21 | -------------------------------------------------------------------------------- /eng/common/core-templates/variables/pool-providers.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | is1ESPipeline: false 3 | 4 | variables: 5 | - ${{ if eq(parameters.is1ESPipeline, 'true') }}: 6 | - template: /eng/common/templates-official/variables/pool-providers.yml 7 | - ${{ else }}: 8 | - template: /eng/common/templates/variables/pool-providers.yml -------------------------------------------------------------------------------- /eng/common/cross/armel/tizen/tizen.patch: -------------------------------------------------------------------------------- 1 | diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so 2 | --- a/usr/lib/libc.so 2016-12-30 23:00:08.284951863 +0900 3 | +++ b/usr/lib/libc.so 2016-12-30 23:00:32.140951815 +0900 4 | @@ -2,4 +2,4 @@ 5 | Use the shared library, but some functions are only in 6 | the static library, so try that secondarily. */ 7 | OUTPUT_FORMAT(elf32-littlearm) 8 | -GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux.so.3 ) ) 9 | +GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux.so.3 ) ) 10 | -------------------------------------------------------------------------------- /eng/common/cross/riscv64/tizen/tizen.patch: -------------------------------------------------------------------------------- 1 | diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so 2 | --- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 3 | +++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 4 | @@ -2,4 +2,4 @@ 5 | Use the shared library, but some functions are only in 6 | the static library, so try that secondarily. */ 7 | OUTPUT_FORMAT(elf64-littleriscv) 8 | -GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-riscv64-lp64d.so.1 ) ) 9 | +GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-riscv64-lp64d.so.1 ) ) 10 | -------------------------------------------------------------------------------- /eng/common/dotnet-install.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0dotnet-install.ps1""" %*" -------------------------------------------------------------------------------- /eng/common/dotnet-install.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(PositionalBinding=$false)] 2 | Param( 3 | [string] $verbosity = 'minimal', 4 | [string] $architecture = '', 5 | [string] $version = 'Latest', 6 | [string] $runtime = 'dotnet', 7 | [string] $RuntimeSourceFeed = '', 8 | [string] $RuntimeSourceFeedKey = '' 9 | ) 10 | 11 | . $PSScriptRoot\tools.ps1 12 | 13 | $dotnetRoot = Join-Path $RepoRoot '.dotnet' 14 | 15 | $installdir = $dotnetRoot 16 | try { 17 | if ($architecture -and $architecture.Trim() -eq 'x86') { 18 | $installdir = Join-Path $installdir 'x86' 19 | } 20 | InstallDotNet $installdir $version $architecture $runtime $true -RuntimeSourceFeed $RuntimeSourceFeed -RuntimeSourceFeedKey $RuntimeSourceFeedKey 21 | } 22 | catch { 23 | Write-Host $_.ScriptStackTrace 24 | Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ 25 | ExitWithExitCode 1 26 | } 27 | 28 | ExitWithExitCode 0 29 | -------------------------------------------------------------------------------- /eng/common/enable-cross-org-publishing.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string] $token 3 | ) 4 | 5 | 6 | . $PSScriptRoot\pipeline-logging-functions.ps1 7 | 8 | # Write-PipelineSetVariable will no-op if a variable named $ci is not defined 9 | # Since this script is only ever called in AzDO builds, just universally set it 10 | $ci = $true 11 | 12 | Write-PipelineSetVariable -Name 'VSS_NUGET_ACCESSTOKEN' -Value $token -IsMultiJobVariable $false 13 | Write-PipelineSetVariable -Name 'VSS_NUGET_URI_PREFIXES' -Value 'https://dnceng.pkgs.visualstudio.com/;https://pkgs.dev.azure.com/dnceng/;https://devdiv.pkgs.visualstudio.com/;https://pkgs.dev.azure.com/devdiv/' -IsMultiJobVariable $false 14 | -------------------------------------------------------------------------------- /eng/common/init-tools-native.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -command "& """%~dp0init-tools-native.ps1""" %*" 3 | exit /b %ErrorLevel% -------------------------------------------------------------------------------- /eng/common/internal/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /eng/common/internal/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /eng/common/msbuild.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(PositionalBinding=$false)] 2 | Param( 3 | [string] $verbosity = 'minimal', 4 | [bool] $warnAsError = $true, 5 | [bool] $nodeReuse = $true, 6 | [switch] $ci, 7 | [switch] $prepareMachine, 8 | [switch] $excludePrereleaseVS, 9 | [string] $msbuildEngine = $null, 10 | [Parameter(ValueFromRemainingArguments=$true)][String[]]$extraArgs 11 | ) 12 | 13 | . $PSScriptRoot\tools.ps1 14 | 15 | try { 16 | if ($ci) { 17 | $nodeReuse = $false 18 | } 19 | 20 | MSBuild @extraArgs 21 | } 22 | catch { 23 | Write-Host $_.ScriptStackTrace 24 | Write-PipelineTelemetryError -Category 'Build' -Message $_ 25 | ExitWithExitCode 1 26 | } 27 | 28 | ExitWithExitCode 0 -------------------------------------------------------------------------------- /eng/common/post-build/nuget-validation.ps1: -------------------------------------------------------------------------------- 1 | # This script validates NuGet package metadata information using this 2 | # tool: https://github.com/NuGet/NuGetGallery/tree/jver-verify/src/VerifyMicrosoftPackage 3 | 4 | param( 5 | [Parameter(Mandatory=$true)][string] $PackagesPath # Path to where the packages to be validated are 6 | ) 7 | 8 | # `tools.ps1` checks $ci to perform some actions. Since the post-build 9 | # scripts don't necessarily execute in the same agent that run the 10 | # build.ps1/sh script this variable isn't automatically set. 11 | $ci = $true 12 | $disableConfigureToolsetImport = $true 13 | . $PSScriptRoot\..\tools.ps1 14 | 15 | try { 16 | & $PSScriptRoot\nuget-verification.ps1 ${PackagesPath}\*.nupkg 17 | } 18 | catch { 19 | Write-Host $_.ScriptStackTrace 20 | Write-PipelineTelemetryError -Category 'NuGetValidation' -Message $_ 21 | ExitWithExitCode 1 22 | } 23 | -------------------------------------------------------------------------------- /eng/common/sdl/NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /eng/common/sdl/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /eng/common/templates-official/job/onelocbuild.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/onelocbuild.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/job/publish-build-assets.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/publish-build-assets.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/job/source-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/source-build.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/job/source-index-stage1.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/source-index-stage1.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/jobs/codeql-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/codeql-build.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/jobs/jobs.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/jobs.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/jobs/source-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/source-build.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates-official/post-build/common-variables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - template: /eng/common/core-templates/post-build/common-variables.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: true 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates-official/post-build/post-build.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - template: /eng/common/core-templates/post-build/post-build.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: true 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} 9 | -------------------------------------------------------------------------------- /eng/common/templates-official/post-build/setup-maestro-vars.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: true 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates-official/steps/component-governance.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/component-governance.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/enable-internal-runtimes.yml: -------------------------------------------------------------------------------- 1 | # Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' 2 | # variable with the base64-encoded SAS token, by default 3 | steps: 4 | - template: /eng/common/core-templates/steps/enable-internal-runtimes.yml 5 | parameters: 6 | is1ESPipeline: true 7 | 8 | ${{ each parameter in parameters }}: 9 | ${{ parameter.key }}: ${{ parameter.value }} 10 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/enable-internal-sources.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/enable-internal-sources.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates-official/steps/generate-sbom.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/generate-sbom.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/get-delegation-sas.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/get-delegation-sas.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/get-federated-access-token.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/get-federated-access-token.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates-official/steps/publish-logs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/publish-logs.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/retain-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/retain-build.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/send-to-helix.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/send-to-helix.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/source-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/source-build.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/steps/source-index-stage1-publish.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml 3 | parameters: 4 | is1ESPipeline: true 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates-official/variables/sdl-variables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in 3 | # sync with the packages.config file. 4 | - name: DefaultGuardianVersion 5 | value: 0.109.0 6 | - name: GuardianPackagesConfigFile 7 | value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config -------------------------------------------------------------------------------- /eng/common/templates/job/onelocbuild.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/onelocbuild.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/job/publish-build-assets.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/publish-build-assets.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/job/source-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/source-build.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/job/source-index-stage1.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/job/source-index-stage1.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/codeql-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/codeql-build.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/jobs.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/jobs.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/jobs/source-build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: /eng/common/core-templates/jobs/source-build.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/post-build/common-variables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - template: /eng/common/core-templates/post-build/common-variables.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: false 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/post-build/post-build.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - template: /eng/common/core-templates/post-build/post-build.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: false 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/post-build/setup-maestro-vars.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml 3 | parameters: 4 | # Specifies whether to use 1ES 5 | is1ESPipeline: false 6 | 7 | ${{ each parameter in parameters }}: 8 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/steps/component-governance.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/component-governance.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/enable-internal-runtimes.yml: -------------------------------------------------------------------------------- 1 | # Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' 2 | # variable with the base64-encoded SAS token, by default 3 | 4 | steps: 5 | - template: /eng/common/core-templates/steps/enable-internal-runtimes.yml 6 | parameters: 7 | is1ESPipeline: false 8 | 9 | ${{ each parameter in parameters }}: 10 | ${{ parameter.key }}: ${{ parameter.value }} 11 | -------------------------------------------------------------------------------- /eng/common/templates/steps/enable-internal-sources.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/enable-internal-sources.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/steps/generate-sbom.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/generate-sbom.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/get-delegation-sas.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/get-delegation-sas.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/get-federated-access-token.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/get-federated-access-token.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} -------------------------------------------------------------------------------- /eng/common/templates/steps/publish-logs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/publish-logs.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/retain-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/retain-build.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/send-to-helix.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/send-to-helix.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/source-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/source-build.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/steps/source-index-stage1-publish.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml 3 | parameters: 4 | is1ESPipeline: false 5 | 6 | ${{ each parameter in parameters }}: 7 | ${{ parameter.key }}: ${{ parameter.value }} 8 | -------------------------------------------------------------------------------- /eng/common/templates/vmr-build-pr.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: 3 | branches: 4 | include: 5 | - main 6 | - release/* 7 | paths: 8 | exclude: 9 | - documentation/* 10 | - README.md 11 | - CODEOWNERS 12 | 13 | variables: 14 | - template: /eng/common/templates/variables/pool-providers.yml@self 15 | 16 | - name: skipComponentGovernanceDetection # we run CG on internal builds only 17 | value: true 18 | 19 | - name: Codeql.Enabled # we run CodeQL on internal builds only 20 | value: false 21 | 22 | resources: 23 | repositories: 24 | - repository: vmr 25 | type: github 26 | name: dotnet/dotnet 27 | endpoint: dotnet 28 | 29 | stages: 30 | - template: /eng/pipelines/templates/stages/vmr-build.yml@vmr 31 | parameters: 32 | isBuiltFromVmr: false 33 | scope: lite 34 | -------------------------------------------------------------------------------- /eng/sdl-tsa-vars.config: -------------------------------------------------------------------------------- 1 | -SourceToolsList @("policheck","credscan") 2 | -ArtifactToolsList @("binskim") 3 | -TsaInstanceURL https://devdiv.visualstudio.com/ 4 | -TsaProjectName DEVDIV 5 | -TsaNotificationEmail dotnetrp@microsoft.com 6 | -TsaCodebaseAdmin REDMOND\karelz 7 | -TsaBugAreaPath "DevDiv\ASP.NET Core\YARP" 8 | -TsaIterationPath DevDiv 9 | -TsaRepositoryName ReverseProxy 10 | -TsaCodebaseName ReverseProxy 11 | -TsaOnboard $True 12 | -TsaPublish $True 13 | -PoliCheckAdditionalRunConfigParams @("UserExclusionPath < $(Build.SourcesDirectory)/s/eng/PoliCheckExclusions.xml") 14 | -------------------------------------------------------------------------------- /eng/yarpapppack/yarpapppack-linux-arm64.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | linux-arm64 5 | Unix 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /eng/yarpapppack/yarpapppack-linux-x64.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | linux-x64 5 | Unix 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "10.0.100-preview.5.25265.106" 4 | }, 5 | "tools": { 6 | "dotnet": "10.0.100-preview.5.25265.106", 7 | "runtimes": { 8 | "dotnet": [ 9 | "8.0.13", 10 | "9.0.2" 11 | ], 12 | "aspnetcore": [ 13 | "8.0.13", 14 | "9.0.2" 15 | ] 16 | } 17 | }, 18 | "msbuild-sdks": { 19 | "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25305.3", 20 | "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25305.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pack.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -restore -build -pack %*" 3 | -------------------------------------------------------------------------------- /pack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source="${BASH_SOURCE[0]}" 4 | 5 | # resolve $SOURCE until the file is no longer a symlink 6 | while [[ -h $source ]]; do 7 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 8 | source="$(readlink "$source")" 9 | 10 | # if $source was a relative symlink, we need to resolve it relative to the path where the 11 | # symlink file was located 12 | [[ $source != /* ]] && source="$scriptroot/$source" 13 | done 14 | 15 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 16 | "$scriptroot/eng/common/build.sh" --build --restore --pack $@ 17 | -------------------------------------------------------------------------------- /restore.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0eng\common\Build.ps1""" -restore %*" -------------------------------------------------------------------------------- /restore.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source="${BASH_SOURCE[0]}" 4 | 5 | # resolve $SOURCE until the file is no longer a symlink 6 | while [[ -h $source ]]; do 7 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 8 | source="$(readlink "$source")" 9 | 10 | # if $source was a relative symlink, we need to resolve it relative to the path where the 11 | # symlink file was located 12 | [[ $source != /* ]] && source="$scriptroot/$source" 13 | done 14 | 15 | scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" 16 | "$scriptroot/eng/common/build.sh" --restore $@ -------------------------------------------------------------------------------- /samples/BasicYarpSample/BasicYarpSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/BasicYarpSample/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | 9 | builder.Services.AddControllers(); 10 | // Add the reverse proxy capability to the server 11 | builder.Services.AddReverseProxy() 12 | // Initialize the reverse proxy from the "ReverseProxy" section of configuration 13 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); 14 | 15 | var app = builder.Build(); 16 | 17 | // Register the reverse proxy routes 18 | app.MapReverseProxy(); 19 | 20 | app.Run(); 21 | -------------------------------------------------------------------------------- /samples/BasicYarpSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "YARP Proxy Sample": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "launchUrl": "", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | true 8 | false 9 | false 10 | 11 | 12 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Combined/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Ingress": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | }, 8 | "applicationUrl": "https://localhost:5021;http://localhost:5020" 9 | }, 10 | "Docker": { 11 | "commandName": "Docker", 12 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 13 | "publishAllPorts": true, 14 | "useSSL": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Combined/Yarp.Kubernetes.IngressController.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | 78d1f3b4-abce-4c5a-b914-3321fab1f8d0 6 | Linux 7 | $('System.TeamProject') != 'internal' 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Combined/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Combined/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "Yarp": { 9 | "ControllerClass": "microsoft.com/ingress-yarp", 10 | "ServerCertificates": false, 11 | "DefaultSslCertificate": "yarp/yarp-ingress-tls" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Ingress/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Ingress": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | }, 8 | "applicationUrl": "https://localhost:5021;http://localhost:5020" 9 | }, 10 | "Docker": { 11 | "commandName": "Docker", 12 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 13 | "publishAllPorts": true, 14 | "useSSL": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Ingress/Yarp.Kubernetes.Ingress.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | b2dc6cd7-acbb-4d65-ad19-74771ff3c80f 6 | Linux 7 | $('System.TeamProject') != 'internal' 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Ingress/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Ingress/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "ControllerUrl": "http://yarp-controller.yarp.svc.cluster.local:8000/api/dispatch" 9 | } 10 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Monitor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Ingress": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | }, 8 | "applicationUrl": "https://localhost:5021;http://localhost:5020" 9 | }, 10 | "Docker": { 11 | "commandName": "Docker", 12 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 13 | "publishAllPorts": true, 14 | "useSSL": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Monitor/README.md: -------------------------------------------------------------------------------- 1 | # Yarp Ingress Monitor 2 | 3 | This directory contains a sample ingress monitor as well as the definition for the Kubernetes manifests for the ingress controller. 4 | 5 | This monitor works in conjunction with the Yarp Ingress. See [README.md](../Ingress/README.md) file for build and deployment instructions. 6 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Monitor/Yarp.Kubernetes.Monitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | 42f98116-26c4-4115-b6af-c5dec1f88c84 6 | Linux 7 | $('System.TeamProject') != 'internal' 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Monitor/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/Monitor/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "Yarp": { 9 | "ControllerClass": "microsoft.com/ingress-yarp" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/backend/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/backend/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/backend/backend.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | Backend 6 | aaa98da6-d0d4-4ad6-9821-f66057413c3a 7 | Linux 8 | ..\..\..\.. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/backend/backend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: backend 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: backend 10 | template: 11 | metadata: 12 | labels: 13 | app: backend 14 | spec: 15 | containers: 16 | - name: backend 17 | image: /backend: 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - containerPort: 80 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: backend 26 | spec: 27 | selector: 28 | app: backend 29 | ports: 30 | - port: 80 31 | targetPort: 80 32 | type: ClusterIP 33 | -------------------------------------------------------------------------------- /samples/KubernetesIngress.Sample/backend/ingress-sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: minimal-ingress 5 | spec: 6 | ingressClassName: yarp 7 | rules: 8 | - http: 9 | paths: 10 | - path: / 11 | pathType: Prefix 12 | backend: 13 | service: 14 | name: backend 15 | port: 16 | number: 80 17 | -------------------------------------------------------------------------------- /samples/Prometheus/HttpLoadApp/HttpLoadApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | Exe 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/Prometheus/ReverseProxy.Metrics-Prometheus.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Prometheus; 7 | using Yarp.Sample; 8 | 9 | var builder = WebApplication.CreateBuilder(args); 10 | 11 | builder.Services.AddControllers(); 12 | builder.Services.AddReverseProxy() 13 | .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); 14 | 15 | // Enable metric collection for all the underlying event counters used by YARP 16 | builder.Services.AddAllPrometheusMetrics(); 17 | 18 | var app = builder.Build(); 19 | 20 | // Add the reverse proxy endpoints based on routes 21 | app.MapReverseProxy(); 22 | 23 | // Add the /Metrics endpoint for prometheus to query on 24 | app.MapMetrics(); 25 | 26 | app.Run(); 27 | -------------------------------------------------------------------------------- /samples/Prometheus/ReverseProxy.Metrics-Prometheus.Sample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44356/", 7 | "sslPort": 44356 8 | } 9 | }, 10 | "profiles": { 11 | "ReverseProxy.Metrics.Prometheus.Sample": { 12 | "commandName": "Project", 13 | "launchBrowser": false, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IIS Express": { 19 | "commandName": "IISExpress", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/Prometheus/ReverseProxy.Metrics-Prometheus.Sample/ReverseProxy.Metrics.Prometheus.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | Exe 6 | Yarp.Sample 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/Prometheus/ReverseProxy.Metrics-Prometheus.Sample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "Microsoft": "Debug", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/Prometheus/graph_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/yarp/1319bedfa0a2d3d4c45f6c83069497a773315aa9/samples/Prometheus/graph_screenshot.png -------------------------------------------------------------------------------- /samples/Prometheus/run10destinations.cmd: -------------------------------------------------------------------------------- 1 | dotnet run --project %~dp0/../SampleServer/SampleServer.csproj --Urls "http://localhost:10000;http://localhost:10001;http://localhost:10002;http://localhost:10003;http://localhost:10004;http://localhost:10005;http://localhost:10006;http://localhost:10007;http://localhost:10008;http://localhost:10009" -------------------------------------------------------------------------------- /samples/Prometheus/run10destinations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | full_path=$(realpath $0) 4 | dir_path=$(dirname $full_path) 5 | samples=$(dirname $dir_path ) 6 | 7 | dotnet run --project $samples/SampleServer/SampleServer.csproj --Urls "http://localhost:10000;http://localhost:10001;http://localhost:10002;http://localhost:10003;http://localhost:10004;http://localhost:10005;http://localhost:10006;http://localhost:10007;http://localhost:10008;http://localhost:10009" -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44356/", 7 | "sslPort": 44356 8 | } 9 | }, 10 | "profiles": { 11 | "ReverseProxy.Auth.Sample": { 12 | "commandName": "Project", 13 | "launchBrowser": false, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IIS Express": { 19 | "commandName": "IISExpress", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/ReverseProxy.Auth.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ReleaseTFMs) 5 | Exe 6 | Yarp.Sample 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/TokenService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Security.Claims; 6 | using System.Threading.Tasks; 7 | 8 | namespace Yarp.Sample 9 | { 10 | internal sealed class TokenService 11 | { 12 | internal Task GetAuthTokenAsync(ClaimsPrincipal user) 13 | { 14 | // we only have tokens for bob 15 | if (string.Equals("Bob", user.Identity.Name)) 16 | { 17 | return Task.FromResult(Guid.NewGuid().ToString()); 18 | } 19 | return Task.FromResult(null); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/Views/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Access Denied"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

You do not have access to this route.

8 |
9 | 10 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/Views/Account/LoggedOut.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Logged Out"; 3 | } 4 | You have been logged out. 5 | -------------------------------------------------------------------------------- /samples/ReverseProxy.Auth.Sample/Views/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Login"; 3 | } 4 | 5 |

Login

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