Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ManickaP committed Oct 19, 2020
1 parent a5184af commit 6bf8b3a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 10 deletions.
49 changes: 43 additions & 6 deletions src/ReverseProxy/Service/Proxy/HttpProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public async Task ProxyAsync(
// Mitigate https://github.com/microsoft/reverse-proxy/issues/255, IIS considers all requests upgradeable.
&& string.Equals("WebSocket", context.Request.Headers[HeaderNames.Upgrade], StringComparison.OrdinalIgnoreCase);

var destinationRequest = CreateRequestMessage(context, destinationPrefix, isUpgradeRequest, proxyOptions.Transforms.RequestTransforms);
var destinationRequest = CreateRequestMessage(context, destinationPrefix, isUpgradeRequest, proxyOptions);

var isClientHttp2 = ProtocolHelper.IsHttp2(context.Request.Protocol);

Expand Down Expand Up @@ -231,7 +231,7 @@ public async Task ProxyAsync(
}
}

private HttpRequestMessage CreateRequestMessage(HttpContext context, string destinationAddress, bool isUpgradeRequest, IReadOnlyList<RequestParametersTransform> transforms)
private HttpRequestMessage CreateRequestMessage(HttpContext context, string destinationAddress, bool isUpgradeRequest, RequestProxyOptions proxyOptions)
{
// "http://a".Length = 8
if (destinationAddress == null || destinationAddress.Length < 8)
Expand All @@ -240,21 +240,40 @@ private HttpRequestMessage CreateRequestMessage(HttpContext context, string dest
}

// Default to HTTP/1.1 for proxying upgradeable requests. This is already the default as of .NET Core 3.1
// Otherwise request HTTP/2 and let HttpClient fallback to HTTP/1.1 if it cannot establish HTTP/2 with the target.
// Otherwise request what's set in proxyOptions (e.g. default HTTP/2) and let HttpClient negotiate the protocol
// based on VersionPolicy (for .NET 5 and higher). For example, downgrading to HTTP/1.1 if it cannot establish HTTP/2 with the target.
// This is done without extra round-trips thanks to ALPN. We can detect a downgrade after calling HttpClient.SendAsync
// (see Step 3 below). TBD how this will change when HTTP/3 is supported.
var httpVersion = isUpgradeRequest ? ProtocolHelper.Http11Version : ProtocolHelper.Http2Version;
var httpVersion = isUpgradeRequest ? ProtocolHelper.Http11Version : proxyOptions.HttpVersion;
#if NET5_0
var httpVersionPolicy = isUpgradeRequest ? HttpVersionPolicy.RequestVersionOrLower : proxyOptions.VersionPolicy;
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif

// TODO Perf: We could probably avoid splitting this and just append the final path and query
UriHelper.FromAbsolute(destinationAddress, out var destinationScheme, out var destinationHost, out var destinationPathBase, out _, out _); // Query and Fragment are not supported here.

var request = context.Request;
var transforms = proxyOptions.Transforms.RequestTransforms;
if (transforms.Count == 0)
{
var url = UriHelper.BuildAbsolute(destinationScheme, destinationHost, destinationPathBase, request.Path, request.QueryString);
Log.Proxying(_logger, url);
var uri = new Uri(url, UriKind.Absolute);
return new HttpRequestMessage(HttpUtilities.GetHttpMethod(context.Request.Method), uri) { Version = httpVersion };
return new HttpRequestMessage(HttpUtilities.GetHttpMethod(context.Request.Method), uri)
{
Version = httpVersion,
#if NET5_0
VersionPolicy = httpVersionPolicy,
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif
};
}

var transformContext = new RequestParametersTransformContext()
Expand All @@ -264,6 +283,14 @@ private HttpRequestMessage CreateRequestMessage(HttpContext context, string dest
Method = request.Method,
Path = request.Path,
Query = new QueryTransformContext(request),
#if NET5_0
VersionPolicy = httpVersionPolicy,
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif

};
foreach (var transform in transforms)
{
Expand All @@ -273,7 +300,17 @@ private HttpRequestMessage CreateRequestMessage(HttpContext context, string dest
var targetUrl = UriHelper.BuildAbsolute(destinationScheme, destinationHost, destinationPathBase, transformContext.Path, transformContext.Query.QueryString);
Log.Proxying(_logger, targetUrl);
var targetUri = new Uri(targetUrl, UriKind.Absolute);
return new HttpRequestMessage(HttpUtilities.GetHttpMethod(transformContext.Method), targetUri) { Version = transformContext.Version };
return new HttpRequestMessage(HttpUtilities.GetHttpMethod(transformContext.Method), targetUri)
{
Version = transformContext.Version,
#if NET5_0
VersionPolicy = transformContext.VersionPolicy,
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif
};
}

private StreamCopyHttpContent SetupRequestBodyCopy(HttpRequest request, HttpRequestMessage destinationRequest,
Expand Down
24 changes: 20 additions & 4 deletions src/ReverseProxy/Service/Proxy/RequestProxyOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the MIT License.

using System;
using System.Net;
using System.Net.Http;
using Microsoft.ReverseProxy.Service.RuntimeModel.Transforms;

namespace Microsoft.ReverseProxy.Service.Proxy
Expand All @@ -22,9 +24,23 @@ public class RequestProxyOptions
/// </summary>
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(100);

// Future:
// ResponseBodyTimeout - The time allowed to receive the full response body. Default to infinite. Not applied to Upgraded requests or gRPC streams.
// HttpVersion - Default to HTTP/2?
// HttpVersionPolicy - Default to OrLower?
/// <summary>
/// Preferred version of the outgoing request.
/// The default is HTTP/2.0.
/// </summary>
public Version HttpVersion { get; set; } = System.Net.HttpVersion.Version20;

#if NET5_0
/// <summary>
/// The policy applied to version selection, e.g. whether to prefer downgrades, upgrades or request an exact version.
/// The default is `RequestVersionOrLower`.
/// </summary>
public HttpVersionPolicy VersionPolicy { get; set; } = HttpVersionPolicy.RequestVersionOrLower;
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Net.Http;
using Microsoft.AspNetCore.Http;

namespace Microsoft.ReverseProxy.Service.RuntimeModel.Transforms
Expand All @@ -21,6 +22,17 @@ public class RequestParametersTransformContext
/// </summary>
public Version Version { get; set; }

#if NET5_0
/// <summary>
/// The HTTP version policy to use for the proxy request.
/// </summary>
public HttpVersionPolicy VersionPolicy { get; set; }
#elif NETCOREAPP3_1
// HttpVersionPolicy didn't exist in .NET Core 3.1 and there's no equivalent.
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif

/// <summary>
/// The HTTP method to use for the proxy request.
/// </summary>
Expand Down

0 comments on commit 6bf8b3a

Please sign in to comment.