diff --git a/NLog.Web.AspNetCore.Tests/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs b/NLog.Web.AspNetCore.Tests/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs new file mode 100644 index 00000000..40f2a9f9 --- /dev/null +++ b/NLog.Web.AspNetCore.Tests/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +#if !ASP_NET_CORE +using System.Web; +using System.Web.Routing; +using System.Collections.Specialized; +using System.Web.SessionState; +#else +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using HttpContextBase = Microsoft.AspNetCore.Http.HttpContext; +#endif +using NLog.Web.LayoutRenderers; +using NSubstitute; +using Xunit; +using System.Net; + +namespace NLog.Web.Tests.LayoutRenderers +{ + public class AspNetRequestIpLayoutRendererTests : TestBase + { + private const string ForwardedForHeader = "X-Forwarded-For"; + + [Fact] + public void ForwardedForHeaderNotPresentRenderRemoteAddress() + { + var httpContext = Substitute.For(); +#if !ASP_NET_CORE + httpContext.Request.ServerVariables.Returns(new NameValueCollection {{"REMOTE_ADDR", "192.0.0.0"}}); + httpContext.Request.Headers.Returns(new NameValueCollection()); +#else + var headers = new HeaderDict(); + httpContext.Request.Headers.Returns(callinfo => headers); + httpContext.Connection.RemoteIpAddress.Returns(callinfo => IPAddress.Parse("192.0.0.0")); +#endif + var renderer = new AspNetRequestIpLayoutRenderer {CheckForwardedForHeader = true}; + renderer.HttpContextAccessor = new FakeHttpContextAccessor(httpContext); + + string result = renderer.Render(new LogEventInfo()); + + Assert.Equal("192.0.0.0", result); + } + + [Fact] + public void ForwardedForHeaderPresentRenderForwardedValue() + { + var httpContext = Substitute.For(); +#if !ASP_NET_CORE + httpContext.Request.ServerVariables.Returns(new NameValueCollection {{"REMOTE_ADDR", "192.0.0.0"}}); + httpContext.Request.Headers.Returns(new NameValueCollection {{ForwardedForHeader, "127.0.0.1"}}); +#else + var headers = new HeaderDict(); + headers.Add(ForwardedForHeader, new StringValues("127.0.0.1")); + httpContext.Request.Headers.Returns(callinfo => headers); +#endif + var renderer = new AspNetRequestIpLayoutRenderer {CheckForwardedForHeader = true}; + renderer.HttpContextAccessor = new FakeHttpContextAccessor(httpContext); + + string result = renderer.Render(new LogEventInfo()); + + Assert.Equal("127.0.0.1", result); + } + + [Fact] + public void ForwardedForHeaderContainsMultipleEntriesRenderFirstValue() + { + var httpContext = Substitute.For(); +#if !ASP_NET_CORE + httpContext.Request.ServerVariables.Returns(new NameValueCollection {{"REMOTE_ADDR", "192.0.0.0"}}); + httpContext.Request.Headers.Returns( + new NameValueCollection {{ForwardedForHeader, "127.0.0.1, 192.168.1.1"}}); +#else + var headers = new HeaderDict(); + headers.Add(ForwardedForHeader, new StringValues("127.0.0.1, 192.168.1.1")); + httpContext.Request.Headers.Returns(callinfo => headers); +#endif + var renderer = new AspNetRequestIpLayoutRenderer {CheckForwardedForHeader = true}; + renderer.HttpContextAccessor = new FakeHttpContextAccessor(httpContext); + + string result = renderer.Render(new LogEventInfo()); + + Assert.Equal("127.0.0.1", result); + } + } +} \ No newline at end of file diff --git a/NLog.Web.AspNetCore/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs b/NLog.Web.AspNetCore/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs index af5c4ab1..3d92aa5d 100644 --- a/NLog.Web.AspNetCore/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs +++ b/NLog.Web.AspNetCore/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs @@ -1,5 +1,8 @@ using System; using System.Text; +#if ASP_NET_CORE +using Microsoft.AspNetCore.Http; +#endif using NLog.LayoutRenderers; using NLog.Web.Internal; @@ -16,18 +19,72 @@ namespace NLog.Web.LayoutRenderers [LayoutRenderer("aspnet-request-ip")] public class AspNetRequestIpLayoutRenderer : AspNetLayoutRendererBase { + private const string ForwardedForHeader = "X-Forwarded-For"; + + /// + /// Gets or sets whether the renderer should check value of X-Forwarded-For header + /// + /// + public bool CheckForwardedForHeader { get; set; } + /// /// Render IP /// protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) { var httpContext = HttpContextAccessor.HttpContext; + + var request = httpContext.TryGetRequest(); + if (request == null) + { + return; + } + + var ip = CheckForwardedForHeader ? TryLookupForwardHeader(request) : string.Empty; + + if (string.IsNullOrEmpty(ip)) + { #if !ASP_NET_CORE - var ip = httpContext.TryGetRequest()?.ServerVariables["REMOTE_ADDR"]; + ip = request.ServerVariables["REMOTE_ADDR"]; #else - var ip = httpContext.Connection?.RemoteIpAddress; + ip = httpContext.Connection?.RemoteIpAddress?.ToString(); #endif + } + builder.Append(ip); } + +#if !ASP_NET_CORE + string TryLookupForwardHeader(System.Web.HttpRequestBase httpRequest) + { + var forwardedHeader = httpRequest.Headers[ForwardedForHeader]; + + if (!string.IsNullOrEmpty(forwardedHeader)) + { + var addresses = forwardedHeader.Split(','); + if (addresses.Length > 0) + { + return addresses[0]; + } + } + + return string.Empty; + } +#else + string TryLookupForwardHeader(HttpRequest httpRequest) + { + if (httpRequest.Headers.ContainsKey(ForwardedForHeader)) + { + var forwardedHeaders = httpRequest.Headers.GetCommaSeparatedValues(ForwardedForHeader); + + if (forwardedHeaders.Length > 0) + { + return forwardedHeaders[0]; + } + } + + return string.Empty; + } +#endif } } \ No newline at end of file diff --git a/NLog.Web.Tests/NLog.Web.Tests.csproj b/NLog.Web.Tests/NLog.Web.Tests.csproj index 46b00463..5a071d99 100644 --- a/NLog.Web.Tests/NLog.Web.Tests.csproj +++ b/NLog.Web.Tests/NLog.Web.Tests.csproj @@ -95,6 +95,9 @@ AspNetRequestFormLayoutRendererTests.cs + + AspNetRequestIpLayoutRendererTests.cs +