Skip to content
Open
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
18 changes: 7 additions & 11 deletions src/OpenRasta.Plugins.ReverseProxy/FluentApiExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using OpenRasta.Configuration;
using OpenRasta.Configuration;
using OpenRasta.Configuration.Fluent;
using OpenRasta.Configuration.Fluent.Extensions;
using OpenRasta.Configuration.MetaModel;
using OpenRasta.Configuration.MetaModel.Handlers;
using OpenRasta.Plugins.ReverseProxy.HttpClientFactory;
using OpenRasta.Plugins.ReverseProxy.HttpMessageHandlers;

Expand All @@ -26,7 +20,7 @@ public static void ReverseProxyFor(this IUriDefinition uriConfiguration, string
public static T ReverseProxy<T>(this T uses, ReverseProxyOptions options = null) where T : IUses
{
options = options ?? new ReverseProxyOptions();

if (options.HttpClient.RoundRobin.Enabled)
{
var handler = options.HttpClient.Handler;
Expand All @@ -37,13 +31,14 @@ public static T ReverseProxy<T>(this T uses, ReverseProxyOptions options = null)
options.HttpClient.RoundRobin.ClientCount,
handler,
options.HttpClient.RoundRobin.LeaseTime);

uses.Dependency(d => d.Singleton(() => new ReverseProxy(
options.Timeout,
options.ForwardedHeaders.ConvertLegacyHeaders,
options.Via.Pseudonym,
factory.GetClient,
options.OnSend
options.OnSend,
options.ForwardedHeaders.ByIdentifierOverride
)));
}
else
Expand All @@ -53,7 +48,8 @@ public static T ReverseProxy<T>(this T uses, ReverseProxyOptions options = null)
options.ForwardedHeaders.ConvertLegacyHeaders,
options.Via.Pseudonym,
options.HttpClient.Factory,
options.OnSend
options.OnSend,
options.ForwardedHeaders.ByIdentifierOverride
)));
}

Expand Down
24 changes: 16 additions & 8 deletions src/OpenRasta.Plugins.ReverseProxy/ReverseProxy.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net.Http;
Expand All @@ -18,17 +17,19 @@ public class ReverseProxy
readonly TimeSpan _timeout;
readonly bool _convertForwardedHeaders;
readonly string _viaIdentifier;
readonly string _byIdentifierOverride;

public ReverseProxy(TimeSpan requestTimeout, bool convertForwardedHeaders, string viaIdentifier,
Func<HttpClient> clientFactory,
Action<ICommunicationContext, HttpRequestMessage> onSend)
Action<ICommunicationContext, HttpRequestMessage> onSend,
string byIdentifierOverride = null)
{
_timeout = requestTimeout;
_httpClient = clientFactory;
_onSend = onSend;
_convertForwardedHeaders = convertForwardedHeaders;
_viaIdentifier = viaIdentifier;

_byIdentifierOverride = byIdentifierOverride;
}

public async Task<ReverseProxyResponse> Send(ICommunicationContext context, string target)
Expand All @@ -42,7 +43,7 @@ public async Task<ReverseProxyResponse> Send(ICommunicationContext context, stri


PrepareRequestBody(context, requestMessage);
PrepareRequestHeaders(context, requestMessage, _convertForwardedHeaders);
PrepareRequestHeaders(context, requestMessage, _convertForwardedHeaders, _byIdentifierOverride);

var viaIdentifier = PrepareViaHeader(context, requestMessage);

Expand Down Expand Up @@ -90,7 +91,7 @@ static void PrepareRequestBody(ICommunicationContext context, HttpRequestMessage
}

static void PrepareRequestHeaders(ICommunicationContext context, HttpRequestMessage request,
bool convertLegacyHeaders)
bool convertLegacyHeaders, string byIdentifierOverride = null)
{
StringBuilder legacyForward = null;

Expand Down Expand Up @@ -142,12 +143,19 @@ void appendParameter(string key, string value)
request.Headers.Add(header.Key, header.Value);
}

var byIdentifier =
byIdentifierOverride ??
(context.PipelineData.ContainsKey("server.localIpAddress")
? context.PipelineData["server.localIpAddress"].ToString()
: $"_{Environment.MachineName}");

if (convertLegacyHeaders && legacyForward?.Length > 0)
{
legacyForward.Append($";by={byIdentifier}");
request.Headers.Add("forwarded", legacyForward.ToString());
}

request.Headers.Add("forwarded", CurrentForwarded(context));
request.Headers.Add("forwarded", CurrentForwarded(context, byIdentifier));
}

static Uri GetProxyTargetUri(TemplatedUriMatch requestUriMatch, string target,
Expand Down Expand Up @@ -192,9 +200,9 @@ static Uri GetProxyTargetUri(TemplatedUriMatch requestUriMatch, string target,
return proxyTargetUri;
}

static string CurrentForwarded(ICommunicationContext context)
static string CurrentForwarded(ICommunicationContext context, string byIdentifier)
{
return $"proto={context.Request.Uri.Scheme};host={context.Request.Uri.Host}";
return $"proto={context.Request.Uri.Scheme};host={context.Request.Uri.Host};by={byIdentifier}";
}
}
}
1 change: 1 addition & 0 deletions src/OpenRasta.Plugins.ReverseProxy/ReverseProxyOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ForwardedHeadersOptions
{
public bool ConvertLegacyHeaders { get; set; }
public bool RunAsForwardedHost { get; set; }
public string ByIdentifierOverride { get; set; }
}

public class HttpClientOptions
Expand Down
34 changes: 33 additions & 1 deletion src/Tests/Plugins.ReverseProxy/Implementation/ProxyApiFrom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ public class ProxyApiFrom : IConfigurationSource
readonly string from;
readonly string to;
readonly ReverseProxyOptions options;
readonly string localIpAddress;

public ProxyApiFrom(
string from,
string to,
ReverseProxyOptions options)
ReverseProxyOptions options,
string localIpAddress = null)
{
this.from = from;
this.to = to;
this.options = options;
this.localIpAddress = localIpAddress;
}

public void Configure()
Expand All @@ -30,6 +33,8 @@ public void Configure()

ResourceSpace.Uses.ReverseProxy(options);
ResourceSpace.Uses.PipelineContributor<WriteServerTimingHeader>();

ResourceSpace.Uses.PipelineContributor(() => new SetLocalIpAddress(localIpAddress));
}
}

Expand All @@ -45,4 +50,31 @@ public void Initialize(IPipeline pipelineRunner)
.Before<KnownStages.IResponseCoding>();
}
}

public class SetLocalIpAddress : IPipelineContributor
{
readonly string localIpAddress;

public SetLocalIpAddress(string localIpAddress)
{
this.localIpAddress = localIpAddress;
}

public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner.Notify(context =>
{
if (localIpAddress != null)
{
context.PipelineData["server.localIpAddress"] = localIpAddress;
}
else
{
context.PipelineData.Remove("server.localIpAddress");
}
return PipelineContinuation.Continue;
})
.Before<KnownStages.IAuthentication>();
}
}
}
16 changes: 10 additions & 6 deletions src/Tests/Plugins.ReverseProxy/Implementation/ProxyServer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -9,7 +10,6 @@
using OpenRasta.Hosting.AspNetCore;
using OpenRasta.Plugins.ReverseProxy;
using OpenRasta.Web;
using Tests.Hosting.Owin;
using HttpMethod = System.Net.Http.HttpMethod;

namespace Tests.Plugins.ReverseProxy.Implementation
Expand All @@ -18,6 +18,7 @@ public class ProxyServer
{
Func<int, string> _fromUri;
Func<int, string> _toUri;
string _fromLocalIpAddress;

Action<ReverseProxyOptions> _fromOptions;
Action<ReverseProxyOptions> _toOptions;
Expand All @@ -32,10 +33,13 @@ public ProxyServer()
_serverFactory = CreateTestServers;
}

public ProxyServer FromServer(string fromUri, Action<ReverseProxyOptions> options = null)
public ProxyServer FromServer(string fromUri,
Action<ReverseProxyOptions> options = null,
string localIpAddress = null)
{
_fromUri = port => fromUri;
_fromOptions = options;
_fromLocalIpAddress = localIpAddress;
return this;
}

Expand All @@ -47,10 +51,10 @@ public ProxyServer FromServer(Func<int, string> fromUri, Action<ReverseProxyOpti
}

public ProxyServer ToServer(
string toUri,
string toUri,
Func<ICommunicationContext, Task<string>> handler = null,
Action<ReverseProxyOptions> options = null
,string resourceRegistrationUri = null)
, string resourceRegistrationUri = null)
{
_toUri = port => toUri;
_toOptions = options;
Expand Down Expand Up @@ -158,7 +162,7 @@ async Task<ProxyResponse> SendAsync(string method, string uri)
(IDisposable host, int port) CreateKestrelFromServer(int toPort)
{
var options = new ReverseProxyOptions();

_fromOptions?.Invoke(options);
var host =
new WebHostBuilder()
Expand Down Expand Up @@ -240,7 +244,7 @@ TestServer CreateFromServer(Func<HttpMessageHandler> httpMessageHandler)
.Configure(app =>
{
app.UseOpenRasta(
new ProxyApiFrom(_fromUri(80), _toUri(80), options));
new ProxyApiFrom(_fromUri(80), _toUri(80), options, _fromLocalIpAddress));
}));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using OpenRasta.Collections;
using Shouldly;
using Tests.Plugins.ReverseProxy.Implementation;
using Xunit;
Expand All @@ -20,7 +25,7 @@ public async Task legacy_is_rewritten()
.GetAsync("proxy"))

{
response.Content.ShouldBe("|host=openrasta.example;proto=https;base=\"/app\",proto=http;host=localhost");
response.Content.ShouldMatch("^\\|host=openrasta.example;proto=https;base=\\\"/app\\\";by=.*,proto=http;host=localhost;by=.*$");
}
}

Expand All @@ -31,9 +36,49 @@ public async Task forwarded_chain_is_preserved()
.FromServer("/proxy")
.ToServer("/proxied", async ctx => ctx.Request.Headers["Forwarded"])
.AddHeader("Forwarded", "host=openrasta.example")
.AddHeader("Forwarded", "host=openrasta.example2")
.GetAsync("proxy"))
{
response.Content.ShouldBe("host=openrasta.example,proto=http;host=localhost");
response.Content.ShouldMatch("^host=openrasta.example,host=openrasta.example2,proto=http;host=localhost;by=.*$");
}
}

[Fact]
public async Task by_is_set_to_owin_local_ip_when_it_is_present()
{
using (var response = await new ProxyServer()
.FromServer("/proxy", localIpAddress: "10.0.10.1")
.ToServer("/proxied", async ctx => ctx.Request.Headers["Forwarded"])
.AddHeader("Forwarded", "host=openrasta.example")
.GetAsync("proxy"))
{
response.Content.ShouldBe("host=openrasta.example,proto=http;host=localhost;by=10.0.10.1");
}
}

[Fact]
public async Task by_is_set_to_configured_override_when_it_is_present()
{
using (var response = await new ProxyServer()
.FromServer("/proxy", options => options.ForwardedHeaders.ByIdentifierOverride = "foo", "10.0.10.1")
.ToServer("/proxied", async ctx => ctx.Request.Headers["Forwarded"])
.AddHeader("Forwarded", "host=openrasta.example")
.GetAsync("proxy"))
{
response.Content.ShouldBe("host=openrasta.example,proto=http;host=localhost;by=foo");
}
}

[Fact]
public async Task by_is_set_to_obfuscated_machine_name_when_no_owin_local_ip_is_present_and_no_override_configured()
{
using (var response = await new ProxyServer()
.FromServer("/proxy")
.ToServer("/proxied", async ctx => ctx.Request.Headers["Forwarded"])
.AddHeader("Forwarded", "host=openrasta.example")
.GetAsync("proxy"))
{
response.Content.ShouldBe($"host=openrasta.example,proto=http;host=localhost;by=_{Environment.MachineName}");
}
}
}
Expand Down