Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4e96dde
Bump branding to rc2 (#1313)
Tratcher Oct 20, 2021
bf1cbf4
Don't cancel the request CTS on request content cancellation (#1320)
MihaZupan Oct 22, 2021
10f4853
Update dependencies from https://github.com/dotnet/arcade build 20211…
dotnet-maestro[bot] Oct 25, 2021
3018577
Build doc comments and include them in the packages #1319 (#1322)
Tratcher Oct 25, 2021
d3a185a
#1280 Fix Dockerfiles for .NET 6 SDK (#1281)
dpbevin Oct 25, 2021
946025a
Add internal/release branches to build script (#1328)
Tratcher Oct 26, 2021
d94f55d
X-Forwarded overwrites by default, not appends (#1329) (#1330)
Tratcher Oct 27, 2021
5e070f1
Servicing docs (#1332)
Tratcher Oct 27, 2021
3c059d5
Fix PathPattern encoding #1307 (#1321)
Tratcher Oct 27, 2021
9eaba9c
Move OperatorFramework UnitTests to xUnit (#1333)
MihaZupan Oct 27, 2021
e811677
Account for header type when removing headers (#1335)
MihaZupan Oct 28, 2021
c222ce8
Adding diagnostics article (#1286)
samsp-msft Oct 28, 2021
83d41b6
Document header concerns #1028 #1116 (#1336)
Tratcher Oct 29, 2021
a99eb32
More client cert docs (#1339)
Tratcher Oct 29, 2021
5635334
Spellcheck (#1340)
Tratcher Oct 29, 2021
ff769d6
Update load-balancing article (#1338)
MihaZupan Oct 30, 2021
a6b19a9
Update dependencies from https://github.com/dotnet/arcade build 20211…
dotnet-maestro[bot] Nov 1, 2021
93e0768
Add article on Distributed Tracing (#1342)
MihaZupan Nov 2, 2021
11a6942
HTTPS & TLS docs #305 (#1341)
Tratcher Nov 4, 2021
0472499
Skip LimiterTests.ManyWaitsStackUp (#1355)
MihaZupan Nov 5, 2021
ae60efa
Remove PreReleaseVersionLabel (#1356)
MihaZupan Nov 5, 2021
08f6dd9
Avoid adding the build number to a release package version (#1359)
MihaZupan Nov 5, 2021
4b52469
Merge branch 'release/1.0' into release-latest-1.0-merge
MihaZupan Nov 8, 2021
1339fc5
Update sample dependencies
MihaZupan Nov 8, 2021
2afe120
Add NuGet feed for yarp 1.0.0
MihaZupan Nov 8, 2021
93ddabb
Update samples (#1361)
MihaZupan Nov 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
README.md
**/ingress.yaml
**/ingress-controller.yaml
**/backend.yaml
**/ingress-sample.yaml
.dotnet
TestResults
2 changes: 2 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<!-- Feed for benchmark infrastructure to restore Microsoft.NETCore.App.Runtime for self-contained builds -->
<add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
<!-- Feed containing the 1.0.0 release of Yarp -->
<add key="yarp-1.0.0" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-microsoft-reverse-proxy-08f6dd95-1/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ trigger:
include:
- main
- release/*
- internal/release/*

pr:
autoCancel: false
Expand Down
6 changes: 3 additions & 3 deletions docs/docfx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ The build will produce a series of HTML files in the `_site` directory. Many of

## Publishing the docs

The docs are automatically built and published by a [GitHub Action](https://github.com/microsoft/reverse-proxy/blob/main/.github/workflows/docfx_build.yml) on every push to `release/docs`. The built `_site` directory is pushed to the `gh-pages` branch and served by [https://microsoft.github.io/reverse-proxy/](https://microsoft.github.io/reverse-proxy/). Maintaining a seperate branch for the released docs allows us to choose when to publish them and with what content, and without modifying the build scripts each release.
The docs are automatically built and published by a [GitHub Action](https://github.com/microsoft/reverse-proxy/blob/main/.github/workflows/docfx_build.yml) on every push to `release/latest`. The built `_site` directory is pushed to the `gh-pages` branch and served by [https://microsoft.github.io/reverse-proxy/](https://microsoft.github.io/reverse-proxy/). Maintaining a seperate branch for the released docs allows us to choose when to publish them and with what content, and without modifying the build scripts each release.

Doc edits for the current public release should go into that release's branch (e.g. `release/1.0.0-preview3`) and merged forward into `main` and `release/docs`.
Doc edits for the current public release should go into that release's branch (e.g. `release/1.0.0-preview3`) and merged forward into `main` and `release/latest`.

When publishing a new product version (e.g. `release/1.0.0-preview4`) `release/latest` should be reset to that position after the docs have been updated.
When publishing a new product version (e.g. `release/1.0.0-preview4`) `release/latest` should be merged to that position after the docs have been updated.
249 changes: 249 additions & 0 deletions docs/docfx/articles/diagnosing-yarp-issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# Diagnosing YARP-based proxies

When using a reverse proxy, there is an additional hop from the client to the proxy, and then from the proxy to destination for things to go wrong. This topic should provide some hints and tips for how to debug and diagnose issues when they occur. It assumes that the proxy is already running, and so does not include problems at startup such as configuration errors.

## Logging

The first step to being able to tell what is going on with YARP is to turn on [logging](Link to https://docs.microsoft.com/aspnet/core/fundamentals/logging/#configure-logging-1). This is a configuration flag so can be changed on the fly. YARP is implemented as a middleware component for ASP.NET Core, so you need to enable logging for both YARP and ASP.NET to get the complete picture of what is going on.

By default ASP.NET will log to the console, and the configuration file can be used to control the level of logging.

```Json
//Sets the Logging level for ASP.NET
"Logging": {
"LogLevel": {
"Default": "Information",
// Uncomment to hide diagnostic messages from runtime and proxy
// "Microsoft": "Warning",
// "Yarp" : "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
```

You want logging infomation from the "Microsoft.AspNetCore.*" and "Yarp.ReverseProxy.*" providers. The example above will emit "Information" level events from both providers to the Console. Changing the level to "Debug" will show additional entries. ASP.NET implements change detection for configuration files, so you can edit the appsettings.json file (or appsettings.development.json if running from Visual Studio) while the project is running and observe changes to the log output.

### Understanding Log entries

The logging output is directly tied to the way that ASP.NET Core processes requests. It's important to realize that as middleware, YARP is relying on much of the ASP.NET functionality to process the requests, for example the following is for the processing of a request with "Debug" mode enabled:

| Level | Log Message | Description |
| ----- | ----------- | ----------- |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[39]<br>Connection id "0HMCD0JK7K51U" accepted.| Connections are independent of requests, so this is a new connection |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[1]<br>Connection id "0HMCD0JK7K51U" started. | |
| info | Microsoft.AspNetCore.Hosting.Diagnostics[1]<br>Request starting HTTP/1.1 GET http://localhost:5000/ - - | This is the incomming request to ASP.NET |
| dbug | Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]<br>Wildcard detected, all requests with hosts will be allowed. | My configuation does not tie endpoints to specific hostnames |
| dbug | Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]<br>1 candidate(s) found for the request path '/' | This shows what possible matches there are for the route |
| dbug | Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]<br> Endpoint 'minimumroute' with route pattern '{\*\*catch-all}' is valid for the request path '/' | The mimimum route from YARPs configuration has matched|
| dbug | Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]<br> Request matched endpoint 'minimumroute' | |
| info | Microsoft.AspNetCore.Routing.EndpointMiddleware[0]<br> Executing endpoint 'minimumroute' | |
| info | Yarp.ReverseProxy.Forwarder.HttpForwarder[9]<br> Proxying to http://www.example.com/ | YARP is proxying the request to example.com |
| info | Microsoft.AspNetCore.Routing.EndpointMiddleware[1]<br> Executed endpoint 'minimumroute' | |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[9]<br> Connection id "0HMCD0JK7K51U" completed keep alive response. | The response has finished, but connection can be kept alive. |
| info | Microsoft.AspNetCore.Hosting.Diagnostics[2]<br>Request finished HTTP/1.1 GET http://localhost:5000/ - - - 200 1256 text/html;+charset=utf-8 12.7797ms| The response completed with status code 200, responding with 1256 bytes as text/html in ~13ms. |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]<br> Connection id "0HMCD0JK7K51U" received FIN. | Diagnostic information about the connection to determine who closed it and how cleanly |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[10]<br> Connection id "0HMCD0JK7K51U" disconnecting. | |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Connections[2]<br> Connection id "0HMCD0JK7K51U" stopped. | |
| dbug | Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]<br> Connection id "0HMCD0JK7K51U" sending FIN because: "The Socket transport's send loop completed gracefully." | |

The above gives general information about the request and how it was processed.

### Using ASP.NET 6 Request Logging

If running on .NET 6, then ASP.NET includes an additional middleware component that can be used to provide more details about the request and response. The `UseHttpLogging` component can be added to the request pipeline. It will add additional entries to the log detailing the incoming and outgoing request headers.

``` C#
// This method gets called by the runtime. Use this method to configure the HTTP request
// pipeline that handles requests
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpLogging();
// Enable endpoint routing, required for the reverse proxy
app.UseRouting();
// Register the reverse proxy routes
app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxy();
});
}
```

For example:

```Console
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
Request:
Protocol: HTTP/1.1
Method: GET
Scheme: http
PathBase:
Path: /
Accept: */*
Host: localhost:5000
User-Agent: curl/7.55.1
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
Response:
StatusCode: 200
Content-Type: text/html; charset=utf-8
Date: Tue, 12 Oct 2021 23:29:20 GMT
Server: ECS,(sec/97A5)
Age: 113258
Cache-Control: [Redacted]
ETag: [Redacted]
Expires: Tue, 19 Oct 2021 23:29:20 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Vary: [Redacted]
Content-Length: 1256
X-Cache: [Redacted]
```

## Using Telemetry Events

The [Metrics sample](https://github.com/microsoft/reverse-proxy/tree/main/samples/ReverseProxy.Metrics.Sample) shows how to listen to events from the different providers that collect telemetry as part of YARP. The most important from a diagnostics perspective are:

* ForwarderTelemetryConsumer
* HttpClientTelemetryConsumer

To use either of these you create a class implementing an interface, such as IForwarderTelemetryConsumer:

```C#
public class ForwarderTelemetry : IForwarderTelemetryConsumer
{

/// Called before forwarding a request.
public void OnForwarderStart(DateTime timestamp, string destinationPrefix)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStart :: Destination prefix: {destinationPrefix}");
}

/// Called after forwarding a request.
public void OnForwarderStop(DateTime timestamp, int statusCode)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStop :: Status: {statusCode}");
}

/// Called before <see cref="OnForwarderStop(DateTime, int)"/> if forwarding the request failed.
public void OnForwarderFailed(DateTime timestamp, ForwarderError error)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderFailed :: Error: {error.ToString()}");
}

/// Called when reaching a given stage of forwarding a request.
public void OnForwarderStage(DateTime timestamp, ForwarderStage stage)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderStage :: Stage: {stage.ToString()}");
}

/// Called periodically while a content transfer is active.
public void OnContentTransferring(DateTime timestamp, bool isRequest, long contentLength, long iops, TimeSpan readTime, TimeSpan writeTime)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnContentTransferring :: Is request: {isRequest}, Content length: {contentLength}, IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}");
}

/// Called after transferring the request or response content.
public void OnContentTransferred(DateTime timestamp, bool isRequest, long contentLength, long iops, TimeSpan readTime, TimeSpan writeTime, TimeSpan firstReadTime)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnContentTransferred :: Is request: {isRequest}, Content length: {contentLength}, IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}");
}

/// Called before forwarding a request.
public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId, string destinationId)
{
Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => OnForwarderInvoke:: Cluster id: {clusterId}, Route Id: { routeId}, Destination: {destinationId}");
}
}
```

And then register the class as part of `Configure Services`, for example:

```C#
public void ConfigureServices(IServiceCollection services)
{
services.AddTelemetryConsumer<ForwarderTelemetry>();

// Add the reverse proxy to capability to the server
var proxyBuilder = services.AddReverseProxy();
// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(Configuration.GetSection("ReverseProxy"));
}
```

Then you will log details on each part of the request, for example:

```Console
Forwarder Telemetry [06:40:48.186] => OnForwarderInvoke:: Cluster id: minimumcluster, Route Id: minimumroute, Destination: example.com
Forwarder Telemetry [06:41:00.269] => OnForwarderStart :: Destination prefix: http://www.example.com/
Forwarder Telemetry [06:41:00.298] => OnForwarderStage :: Stage: SendAsyncStart
Forwarder Telemetry [06:41:00.507] => OnForwarderStage :: Stage: SendAsyncStop
Forwarder Telemetry [06:41:00.530] => OnForwarderStage :: Stage: ResponseContentTransferStart
Forwarder Telemetry [06:41:03.655] => OnForwarderStop :: Status: 200
```

The events for Telemetry are fired as they occur, so you can [fish out the HttpContext](https://docs.microsoft.com/aspnet/core/fundamentals/http-context#use-httpcontext-from-custom-components) and the YARP feature from it:

``` C#
public void ConfigureServices(IServiceCollection services)
{
services.AddTelemetryConsumer<ForwarderTelemetry>();
services.AddHttpContextAccessor();
...
}

public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId, string destinationId)
{
var context = new HttpContextAccessor().HttpContext;
var YarpFeature = context.GetReverseProxyFeature();

var dests = from d in YarpFeature.AvailableDestinations
select d.Model.Config.Address;

Console.WriteLine($"Destinations: {string.Join(", ", dests)}");
}
```

## Using custom middleware

Another way to inspect the state for requests is to insert additional middleware into the request pipeline. You can insert between the other stages to see the state of the request.

```C#
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// We can customize the proxy pipeline and add/remove/replace steps
endpoints.MapReverseProxy(proxyPipeline =>
{
// Use a custom proxy middleware, defined below
proxyPipeline.Use(MyCustomProxyStep);
// Don't forget to include these two middleware when you make a custom proxy pipeline (if you need them).
proxyPipeline.UseSessionAffinity();
proxyPipeline.UseLoadBalancing();
});
});
}

public Task MyCustomProxyStep(HttpContext context, Func<Task> next)
{
// Can read data from the request via the context
foreach (var header in context.Request.Headers)
{
Console.WriteLine($"{header.Key}: {header.Value}");
}

// The context also stores a ReverseProxyFeature which holds proxy specific data such as the cluster, route and destinations
var proxyFeature = context.GetReverseProxyFeature();
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(proxyFeature.Route.Config));

// Important - required to move to the next step in the proxy pipeline
return next();
}
```

You can also use [ASP.NET middleware](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/write) within Configure that will enable you to inspect the request before the proxy pipeline.

> **Note:** The proxy will stream the response from the destination server back to the client, so the response headers and body are not readily accessible via middleware.

## Using the debugger

A debugger, such as Visual Studio can be attached to the proxy process. However, unless you have existing middleware, there is not a good place in the app code to break and inspect the state of the request. Therefore the debugger is best used in conjunction with one of the techniques above so that you have distinct places to insert breakpoints etc.
30 changes: 30 additions & 0 deletions docs/docfx/articles/distributed-tracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

# Distributed tracing

As an ASP.NET Core component, YARP can easily integrate into different tracing systems the same as any other web application.
See detailed guides for setting up your application with:
- [OpenTelemetry] or
- [Application Insights]

.NET 6.0 has built-in configurable support for distributed tracing that YARP takes advantage of to enable such scenarios out-of-the-box.

## .NET 5.0 and older

Before 6.0, `SocketsHttpHandler` could not be used with distributed tracing.
When running on .NET 3.1 or 5.0, YARP will copy tracing headers as-is, not accounting for any changes that may have occurred to the trace within the application.

To get YARP to actively participate, you must use a workaround to manually insert the correct headers.

The recommended workaround is to:
- Include a [custom `IForwarderHttpClientFactory`][DiagnosticsHandlerFactory] in your project and
- Register it in the DI container
```c#
#if !NET6_0_OR_GREATER
services.AddSingleton<IForwarderHttpClientFactory, DiagnosticsHandlerFactory>();
#endif
```
The workaround mimics the behavior of the internal `DiagnosticsHandler` class used by `HttpClient`. As such, it automatically works with instrumentation packages from OpenTelemetry or Application Insights.

[OpenTelemetry]: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/trace/getting-started/README.md
[Application Insights]: https://docs.microsoft.com/azure/azure-monitor/app/asp-net-core
[DiagnosticsHandlerFactory]: https://github.com/microsoft/reverse-proxy/blob/main/samples/ReverseProxy.Code.Sample/DiagnosticsHandlerFactory.cs
4 changes: 2 additions & 2 deletions docs/docfx/articles/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Or create a new ASP.NET Core web application in Visual Studio 2022, and choose "

```XML
<ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0-rc.1.*" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
</ItemGroup>
```

Expand Down Expand Up @@ -158,7 +158,7 @@ You can find out more about the available configuration options by looking at [R
"Clusters": {
"cluster1": {
"Destinations": {
"cluster1/destination1": {
"destination1": {
"Address": "https://example.com/"
}
}
Expand Down
Loading