Skip to content

Use the same default transformer for HttpForwarder and the whole YARP #1246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 4 deletions samples/ReverseProxy.Direct.Sample/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# YARP Direct Proxy Example

Some customers who have an existing custom proxy for HTTP/1.1 are looking at YARP for a solution to handle more complex requests, such as HTTP/2, gRPC, WebSockets in future QUIC and HTTP/3. These applications have their own means of routing, load balancing, affinity, etc. and only need to forward a specific request to a specific destination. To make it easier to integrate YARP into these scenarios, the component that proxies requests is exposed via IHttpProxy which can be called directly, and has few dependencies on the rest of YARP's infrastructure.
Some customers who have an existing custom proxy for HTTP/1.1 are looking at YARP for a solution to handle more complex requests, such as HTTP/2, gRPC, WebSockets in future QUIC and HTTP/3. These applications have their own means of routing, load balancing, affinity, etc. and only need to forward a specific request to a specific destination. To make it easier to integrate YARP into these scenarios, the component that proxies requests is exposed via IHttpForwarder which can be called directly, and has few dependencies on the rest of YARP's infrastructure.

This example shows how to use IHTTPProxy to proxy a request to/from a specified destination.
This example shows how to use IHttpForwarder to proxy a request to/from a specified destination.


The operation of the proxy can be thought of as:
Expand Down Expand Up @@ -35,9 +35,9 @@ Normal proxying comprises the following steps:
| 9 | Copy response trailer headers and finish response | Client ◄-- Proxy ◄-- Destination |
| 10 | Wait for completion of step 2: copying request body | Client --► Proxy --► Destination |

To enable control over mapping request and response fields and headers between the client and destination (steps 4 and 7 above), the HttpProxy.ProxyAsync method takes a HttpTransformer. Your implementation can modify the request url, method, protocol version, response status code, or decide which headers are copied, modify them, or insert additional headers as required.
To enable control over mapping request and response fields and headers between the client and destination (steps 4 and 7 above), the HttpForwarder.ProxyAsync method takes a HttpTransformer. Your implementation can modify the request url, method, protocol version, response status code, or decide which headers are copied, modify them, or insert additional headers as required.

**Note:** When using the HttpProxy class directly there are no transforms included by default, you have full control of the transforms in your HttpTransformer implementation. The alternate YARP pipeline model (see BasicYarpSample), has some [default header transforms](https://microsoft.github.io/reverse-proxy/articles/transforms.html), such as adding ```X-Forwarded-For``` and removing the original Host header.
**Note:** When using the HttpForwarder class directly there are some [header transforms](https://microsoft.github.io/reverse-proxy/articles/transforms.html) included by default, such as adding ```X-Forwarded-For``` and removing the original Host header. This is the same set of transforms included by default in the YARP pipeline model (see BasicYarpSample).

## Files

Expand Down
12 changes: 9 additions & 3 deletions src/ReverseProxy/Forwarder/HttpTransformer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Yarp.ReverseProxy.Transforms.Builder;

namespace Yarp.ReverseProxy.Forwarder
{
public class HttpTransformer
{
/// <summary>
/// A default set of transforms that copies all request and response fields and headers, except for some
/// protocol specific values.
/// A default set of transforms that adds X-Forwarded-* headers, removes the original Host value and
/// copies all other request and response fields and headers, except for some protocol specific values.
/// </summary>
public static readonly HttpTransformer Default = new HttpTransformer();
public static readonly HttpTransformer Default;

static HttpTransformer()
{
Default = TransformBuilder.CreateTransformer(new TransformBuilderContext());
}

/// <summary>
/// Used to create derived instances.
Expand Down
2 changes: 1 addition & 1 deletion src/ReverseProxy/Transforms/Builder/TransformBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ internal StructuredTransformer CreateInternal(Action<TransformBuilderContext> ac
return CreateTransformer(context);
}

private static StructuredTransformer CreateTransformer(TransformBuilderContext context)
internal static StructuredTransformer CreateTransformer(TransformBuilderContext context)
{
// RequestHeaderOriginalHostKey defaults to false, and CopyRequestHeaders defaults to true.
// If RequestHeaderOriginalHostKey was not specified then we need to make sure the transform gets
Expand Down
4 changes: 2 additions & 2 deletions test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task NormalRequest_Works()
Assert.Equal(HttpMethod.Post, request.Method);
Assert.Equal(targetUri, request.RequestUri.AbsoluteUri);
Assert.Contains("request", request.Headers.GetValues("x-ms-request-test"));
Assert.Equal("example.com:3456", request.Headers.Host);
Assert.Null(request.Headers.Host);
Assert.False(request.Headers.TryGetValues(":authority", out var value));

Assert.NotNull(request.Content);
Expand Down Expand Up @@ -340,7 +340,7 @@ public async Task UpgradableRequest_Works(string upgradeHeader)
Assert.Equal(HttpMethod.Get, request.Method);
Assert.Equal(targetUri, request.RequestUri.AbsoluteUri);
Assert.Contains("request", request.Headers.GetValues("x-ms-request-test"));
Assert.Equal("example.com:3456", request.Headers.Host);
Assert.Null(request.Headers.Host);
Assert.False(request.Headers.TryGetValues(":authority", out var value));

Assert.Null(request.Content);
Expand Down
6 changes: 3 additions & 3 deletions test/ReverseProxy.Tests/Forwarder/HttpTransformerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public async Task TransformRequestAsync_RemovesRestrictedHeaders()
{
var transformer = HttpTransformer.Default;
var httpContext = new DefaultHttpContext();
var proxyRequest = new HttpRequestMessage();
var proxyRequest = new HttpRequestMessage(HttpMethod.Get, "https://localhost");

foreach (var header in RestrictedHeaders)
{
Expand All @@ -65,7 +65,7 @@ public async Task TransformRequestAsync_TETrailers_Copied()
var transformer = HttpTransformer.Default;
var httpContext = new DefaultHttpContext();
httpContext.Request.Protocol = "HTTP/2";
var proxyRequest = new HttpRequestMessage();
var proxyRequest = new HttpRequestMessage(HttpMethod.Get, "https://localhost");

httpContext.Request.Headers[HeaderNames.TE] = "traiLers";

Expand All @@ -83,7 +83,7 @@ public async Task TransformRequestAsync_ContentLengthAndTransferEncoding_Content
{
var transformer = HttpTransformer.Default;
var httpContext = new DefaultHttpContext();
var proxyRequest = new HttpRequestMessage()
var proxyRequest = new HttpRequestMessage(HttpMethod.Get, "https://localhost")
{
Content = new ByteArrayContent(Array.Empty<byte>())
};
Expand Down