Skip to content

Commit

Permalink
Merge pull request #244 from adbrowne/reverseproxy
Browse files Browse the repository at this point in the history
Support for X-Forwarded headers
  • Loading branch information
gregoryyoung committed Oct 13, 2014
2 parents d54edf4 + 2fd3b2b commit 3709093
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/EventStore.Core.Tests/EventStore.Core.Tests.csproj
Expand Up @@ -417,6 +417,7 @@
<Compile Include="Services\Transport\Http\PortableServer.cs" />
<Compile Include="Services\Transport\Http\ping_controller_should.cs" />
<Compile Include="MightyMooseIgnoreAttribute.cs" />
<Compile Include="Services\Transport\Http\proxy_headers.cs" />
<Compile Include="Services\Transport\Http\speed_test.cs" />
<Compile Include="Services\Transport\Http\uri_router_should.cs" />
<Compile Include="Services\Transport\Tcp\ssl_connection.cs" />
Expand Down
55 changes: 55 additions & 0 deletions src/EventStore.Core.Tests/Services/Transport/Http/proxy_headers.cs
@@ -0,0 +1,55 @@
using System;
using System.Collections.Specialized;
using EventStore.Transport.Http.EntityManagement;
using NUnit.Framework;

namespace EventStore.Core.Tests.Services.Transport.Http
{
[TestFixture]
class proxy_headers
{
[Test]
public void with_no_headers_uri_is_unchanged()
{
var inputUri = new Uri("http://www.example.com:1234/path/?key=value#anchor");
var requestedUri =
HttpEntity.BuildRequestedUrl(inputUri,
new NameValueCollection());

Assert.AreEqual(inputUri, requestedUri);
}

[Test]
public void with_port_forward_header_only_port_is_changed()
{
var inputUri = new Uri("http://www.example.com:1234/path/?key=value#anchor");
var headers = new NameValueCollection { { "X-Forwarded-Port", "4321" } };
var requestedUri =
HttpEntity.BuildRequestedUrl(inputUri, headers);

Assert.AreEqual(new Uri("http://www.example.com:4321/path/?key=value#anchor"), requestedUri);
}

[Test]
public void non_integer_port_forward_header_is_ignored()
{
var inputUri = new Uri("http://www.example.com:1234/path/?key=value#anchor");
var headers = new NameValueCollection { { "X-Forwarded-Port", "abc" } };
var requestedUri =
HttpEntity.BuildRequestedUrl(inputUri, headers);

Assert.AreEqual(inputUri, requestedUri);
}

[Test]
public void with_proto_forward_header_only_scheme_is_changed()
{
var inputUri = new Uri("http://www.example.com:1234/path/?key=value#anchor");
var headers = new NameValueCollection { { "X-Forwarded-Proto", "https" } };
var requestedUri =
HttpEntity.BuildRequestedUrl(inputUri, headers);

Assert.AreEqual(new Uri("https://www.example.com:1234/path/?key=value#anchor"), requestedUri);
}
}
}
Expand Up @@ -64,7 +64,7 @@ private static void RegisterRedirectAction(IHttpService service, string fromUrl,
new[]
{
new KeyValuePair<string, string>(
"Location", new Uri(match.BaseUri, toUrl).AbsoluteUri)
"Location", new Uri(http.HttpEntity.RequestedUrl, toUrl).AbsoluteUri)
}, Console.WriteLine));
}
}
Expand Down
Expand Up @@ -235,7 +235,7 @@ private void OnProjectionStatusGet(HttpEntityManager http, UriTemplateMatch matc
new SendToHttpWithConversionEnvelope
<ProjectionManagementMessage.Statistics, ProjectionStatisticsHttpFormatted>(
_networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator,
status => new ProjectionStatisticsHttpFormatted(status.Projections[0], s => MakeUrl(match, s)),
status => new ProjectionStatisticsHttpFormatted(status.Projections[0], s => MakeUrl(http, s)),
ErrorsEnvelope(http));
Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, null, match.BoundVariables["name"], true));
}
Expand Down Expand Up @@ -263,7 +263,7 @@ private void OnProjectionStatisticsGet(HttpEntityManager http, UriTemplateMatch
new SendToHttpWithConversionEnvelope
<ProjectionManagementMessage.Statistics, ProjectionsStatisticsHttpFormatted>(
_networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator,
status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(match, s)),
status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(http, s)),
ErrorsEnvelope(http));
Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, null, match.BoundVariables["name"], true));
}
Expand Down Expand Up @@ -335,7 +335,7 @@ private void ProjectionsGet(HttpEntityManager http, UriTemplateMatch match, Proj
var envelope =
new SendToHttpWithConversionEnvelope<ProjectionManagementMessage.Statistics, ProjectionsStatisticsHttpFormatted>(
_networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator,
status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(match, s)),
status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(http, s)),
ErrorsEnvelope(http));
Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, mode, null, true));
}
Expand All @@ -349,7 +349,7 @@ private void ProjectionsPost(HttpEntityManager http, UriTemplateMatch match, Pro
_networkSendQueue, http, DefaultFormatter, (codec, message) =>
{
var localPath = string.Format("/projection/{0}", message.Name);
var url = MakeUrl(match, localPath);
var url = MakeUrl(http, localPath);
return new ResponseConfiguration(
201, "Created", codec.ContentType, codec.Encoding, new KeyValuePair<string, string>("Location", url));
}, ErrorsEnvelope(http));
Expand Down Expand Up @@ -544,11 +544,6 @@ private string ConflictFormatter(ICodec codec, ProjectionManagementMessage.Opera
return message.Reason;
}

private static string MakeUrl(UriTemplateMatch match, string localPath)
{
return new Uri(match.BaseUri, localPath).AbsoluteUri;
}

private static string DefaultFormatter<T>(ICodec codec, T message)
{
return codec.To(message);
Expand Down
27 changes: 26 additions & 1 deletion src/EventStore.Transport.Http/EntityManagement/HttpEntity.cs
@@ -1,4 +1,5 @@
using System;
using System.Collections.Specialized;
using System.Net;
using System.Security.Principal;
using EventStore.Common.Utils;
Expand All @@ -19,12 +20,36 @@ public HttpEntity(HttpListenerRequest request, HttpListenerResponse response, IP
Ensure.NotNull(request, "request");
Ensure.NotNull(response, "response");

RequestedUrl = request.Url;
RequestedUrl = BuildRequestedUrl(request.Url, request.Headers);
Request = request;
Response = response;
User = user;
}

public static Uri BuildRequestedUrl(Uri requestUrl, NameValueCollection requestHeaders)
{
var uriBuilder = new UriBuilder(requestUrl);

var forwardedPortHeaderValue = requestHeaders[ProxyHeaders.XForwardedPort];

if (!string.IsNullOrEmpty(forwardedPortHeaderValue))
{
int requestPort;
if (Int32.TryParse(forwardedPortHeaderValue, out requestPort))
{
uriBuilder.Port = requestPort;
}
}

var forwardedProtoHeaderValue = requestHeaders[ProxyHeaders.XForwardedProto];
if (!string.IsNullOrEmpty(forwardedProtoHeaderValue))
{
uriBuilder.Scheme = forwardedProtoHeaderValue;
}

return uriBuilder.Uri;
}

private HttpEntity(IPrincipal user)
{
RequestedUrl = null;
Expand Down
Expand Up @@ -68,6 +68,7 @@
<Compile Include="ICodec.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="HttpStatusCode.cs" />
<Compile Include="ProxyHeaders.cs" />
<Compile Include="Server\HttpAsyncServer.cs" />
<Compile Include="WebRequestExtensions.cs" />
</ItemGroup>
Expand Down
14 changes: 14 additions & 0 deletions src/EventStore.Transport.Http/ProxyHeaders.cs
@@ -0,0 +1,14 @@
namespace EventStore.Transport.Http
{
public static class ProxyHeaders
{
public const string XForwardedPort = "X-Forwarded-Port";
public const string XForwardedProto = "X-Forwarded-Proto";
}

public static class ProxyHeaderValues
{
public const string XForwardedProtoHttp = "http";
public const string XForwardedProtoHttps = "https";
}
}

0 comments on commit 3709093

Please sign in to comment.