diff --git a/CHANGELOG.MD b/CHANGELOG.MD
index 854de575..a029ef21 100644
--- a/CHANGELOG.MD
+++ b/CHANGELOG.MD
@@ -4,10 +4,20 @@ See also [releases](https://github.com/NLog/NLog.Web/releases) and [milestones](
Date format: (year/month/day)
+### v4.9.2-aspnetcore (2020/04/20)
+- [#546](https://github.com/NLog/NLog.Web/pull/546) ${aspnet-request-ip}: make ForwardedForHeader configurable (@304NotModified)
+- [#546](https://github.com/NLog/NLog.Web/pull/546) ${aspnet-request-url} Added UseRawTarget option (@sm-g, @304NotModified)
+
+### v4.9.2-aspnet4 (2020/04/20)
+- [#546](https://github.com/NLog/NLog.Web/pull/546) ${aspnet-request-ip}: make ForwardedForHeader configurable (@304NotModified)
+
### v4.9.1-aspnetcore (2020/03/25)
-- [#524](https://github.com/NLog/NLog.Web/pull/524) ${aspnet-traceidentifier} should automatically check Activity.Current.Id for NetCore3 (@snakefoot)
+- [#524](https://github.com/NLog/NLog.Web/pull/524) ${aspnet-traceidentifier} should automatically check Activity.Current.Id for .NET Core 3 (@snakefoot)
- [#527](https://github.com/NLog/NLog.Web/pull/527) Improved AddNLog with LoggingConfiguration to handle custom LogFactory (@snakefoot)
+### v4.9.1-aspnet4 (2020/03/25)
+- Updated dependencies
+
### v4.9.0-aspnetcore (2019/10/11)
This version adds support for ASP.NET Core 3 and some nice renderers has been added and improved!
diff --git a/appveyor.yml b/appveyor.yml
index d9dfcefd..55d293c3 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 4.9.1.{build}
+version: 4.9.2.{build}
clone_folder: c:\projects\nlogweb
configuration: Release
image: Visual Studio 2019
@@ -22,7 +22,7 @@ skip_tags: true
build_script:
- ps: |
- $versionPrefix = "4.9.1"
+ $versionPrefix = "4.9.2"
$versionSuffix = ""
$versionBuild = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER}
$versionNuget = $versionPrefix
diff --git a/src/NLog.Web.AspNetCore/NLog.Web.AspNetCore.csproj b/src/NLog.Web.AspNetCore/NLog.Web.AspNetCore.csproj
index 40a1b424..76836095 100644
--- a/src/NLog.Web.AspNetCore/NLog.Web.AspNetCore.csproj
+++ b/src/NLog.Web.AspNetCore/NLog.Web.AspNetCore.csproj
@@ -20,8 +20,10 @@ Supported platforms:
NLog.Web.AspNetCore
logging;log;NLog;web;aspnet;aspnetcore;MVC;Microsoft.Extensions.Logging;httpcontext;session
-- ${aspnet-traceidentifier} should automatically check Activity.Current.Id for NetCore3 (@snakefoot)
-- Improved AddNLog with LoggingConfiguration to handle custom LogFactory (@snakefoot)
+- ${aspnet-request-ip}: make ForwardedForHeader configurable (@304NotModified)
+- ${aspnet-request-url} Added UseRawTarget option (@sm-g, @304NotModified)
+
+See also https://github.com/NLog/NLog.Web/releases
https://nlog-project.org/N.png
https://github.com/NLog/NLog.Web
diff --git a/src/NLog.Web/NLog.Web.csproj b/src/NLog.Web/NLog.Web.csproj
index fb0bbbed..9a7c6798 100644
--- a/src/NLog.Web/NLog.Web.csproj
+++ b/src/NLog.Web/NLog.Web.csproj
@@ -19,6 +19,8 @@ For ASP.NET Core: Check https://www.nuget.org/packages/NLog.Web.AspNetCore
nlog;log;logging;target;layoutrenderer;web;asp.net;MVC;httpcontext
+ See https://github.com/NLog/NLog.Web/releases
+
https://nlog-project.org/N.png
https://github.com/NLog/NLog.Web
@@ -36,7 +38,7 @@ For ASP.NET Core: Check https://www.nuget.org/packages/NLog.Web.AspNetCore
true
-
+
diff --git a/src/Shared/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs b/src/Shared/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs
index fc09be8d..f582a3f7 100644
--- a/src/Shared/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs
+++ b/src/Shared/LayoutRenderers/AspNetRequestIpLayoutRenderer.cs
@@ -1,11 +1,15 @@
using System;
+using System.ComponentModel;
using System.Text;
+
using NLog.Config;
using NLog.LayoutRenderers;
+using NLog.Layouts;
using NLog.Web.Internal;
#if ASP_NET_CORE
using Microsoft.AspNetCore.Http;
-
+#else
+using System.Web;
#endif
namespace NLog.Web.LayoutRenderers
@@ -22,10 +26,14 @@ namespace NLog.Web.LayoutRenderers
[ThreadSafe]
public class AspNetRequestIpLayoutRenderer : AspNetLayoutRendererBase
{
- private const string ForwardedForHeader = "X-Forwarded-For";
+ ///
+ /// The header name to check for the Forwarded-For. Default "X-Forwarded-For". Needs
+ ///
+ [DefaultValue("X-Forwarded-For")]
+ public Layout ForwardedForHeader { get; set; } = "X-Forwarded-For";
///
- /// Gets or sets whether the renderer should check value of X-Forwarded-For header
+ /// Gets or sets whether the renderer should check value of header
///
///
public bool CheckForwardedForHeader { get; set; }
@@ -43,7 +51,7 @@ protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
return;
}
- var ip = CheckForwardedForHeader ? TryLookupForwardHeader(request) : string.Empty;
+ var ip = CheckForwardedForHeader && ForwardedForHeader != null ? TryLookupForwardHeader(request, logEvent) : string.Empty;
if (string.IsNullOrEmpty(ip))
{
@@ -58,9 +66,10 @@ protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
}
#if !ASP_NET_CORE
- string TryLookupForwardHeader(System.Web.HttpRequestBase httpRequest)
+ string TryLookupForwardHeader(HttpRequestBase httpRequest, LogEventInfo logEvent)
{
- var forwardedHeader = httpRequest.Headers[ForwardedForHeader];
+ var headerName = ForwardedForHeader.Render(logEvent);
+ var forwardedHeader = httpRequest.Headers[headerName];
if (!string.IsNullOrEmpty(forwardedHeader))
{
@@ -74,11 +83,12 @@ string TryLookupForwardHeader(System.Web.HttpRequestBase httpRequest)
return string.Empty;
}
#else
- private string TryLookupForwardHeader(HttpRequest httpRequest)
+ private string TryLookupForwardHeader(HttpRequest httpRequest, LogEventInfo logEvent)
{
- if (httpRequest.Headers?.ContainsKey(ForwardedForHeader) == true)
+ var headerName = ForwardedForHeader.Render(logEvent);
+ if (httpRequest.Headers?.ContainsKey(headerName) == true)
{
- var forwardedHeaders = httpRequest.Headers.GetCommaSeparatedValues(ForwardedForHeader);
+ var forwardedHeaders = httpRequest.Headers.GetCommaSeparatedValues(headerName);
if (forwardedHeaders.Length > 0)
{
return forwardedHeaders[0];
diff --git a/src/Shared/LayoutRenderers/AspNetRequestUrlRenderer.cs b/src/Shared/LayoutRenderers/AspNetRequestUrlRenderer.cs
index 3ba59e11..6c60aeca 100644
--- a/src/Shared/LayoutRenderers/AspNetRequestUrlRenderer.cs
+++ b/src/Shared/LayoutRenderers/AspNetRequestUrlRenderer.cs
@@ -5,6 +5,7 @@
using NLog.Web.Internal;
#if ASP_NET_CORE
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
#else
using System.Collections.Specialized;
@@ -24,7 +25,7 @@ namespace NLog.Web.LayoutRenderers
/// ${aspnet-request-url:IncludePort=true} - produces http://www.exmaple.com:80/
/// ${aspnet-request-url:IncludePort=false} - produces http://www.exmaple.com/
/// ${aspnet-request-url:IncludeScheme=false} - produces www.exmaple.com/
- /// ${aspnet-request-url:IncludePort=true:IncludeQueryString=true} - produces http://www.exmaple.com:80/?t=1
+ /// ${aspnet-request-url:IncludePort=true:IncludeQueryString=true} - produces http://www.exmaple.com:80/?t=1
///
///
[LayoutRenderer("aspnet-request-url")]
@@ -37,7 +38,7 @@ public class AspNetRequestUrlRenderer : AspNetLayoutRendererBase
public bool IncludeQueryString { get; set; } = false;
///
- /// To specify whether to include /exclude the Port. Default is false.
+ /// To specify whether to include / exclude the Port. Default is false.
///
public bool IncludePort { get; set; } = false;
@@ -51,6 +52,16 @@ public class AspNetRequestUrlRenderer : AspNetLayoutRendererBase
///
public bool IncludeScheme { get; set; } = true;
+#if ASP_NET_CORE
+
+ ///
+ /// To specify whether to use raw path and full query. Default is false.
+ /// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features.ihttprequestfeature.rawtarget
+ ///
+ public bool UseRawTarget { get; set; } = false;
+
+#endif
+
///
/// Renders the Request URL from the HttpRequest
///
@@ -68,6 +79,7 @@ protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
}
#if !ASP_NET_CORE
+
private void RenderUrl(HttpRequestBase httpRequest, StringBuilder builder)
{
var url = httpRequest.Url;
@@ -112,13 +124,22 @@ private void RenderUrl(HttpRequest httpRequest, StringBuilder builder)
builder.Append(httpRequest.Host.Port.Value);
}
- builder.Append(httpRequest.PathBase.ToUriComponent());
- builder.Append(httpRequest.Path.ToUriComponent());
- if (IncludeQueryString)
+ IHttpRequestFeature httpRequestFeature;
+ if (UseRawTarget && (httpRequestFeature = httpRequest.HttpContext.Features.Get()) != null)
+ {
+ builder.Append(httpRequestFeature.RawTarget);
+ }
+ else
{
- builder.Append(httpRequest.QueryString.Value);
+ builder.Append(httpRequest.PathBase.ToUriComponent());
+ builder.Append(httpRequest.Path.ToUriComponent());
+
+ if (IncludeQueryString)
+ {
+ builder.Append(httpRequest.QueryString.Value);
+ }
}
}
#endif
}
-}
\ No newline at end of file
+}
diff --git a/tests/NLog.Web.Tests/NLog.Web.Tests.csproj b/tests/NLog.Web.Tests/NLog.Web.Tests.csproj
index 66fb5265..43a02a5a 100644
--- a/tests/NLog.Web.Tests/NLog.Web.Tests.csproj
+++ b/tests/NLog.Web.Tests/NLog.Web.Tests.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/tests/Shared/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs b/tests/Shared/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs
index c34ac0bb..ea342000 100644
--- a/tests/Shared/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs
+++ b/tests/Shared/LayoutRenderers/AspNetRequestIpLayoutRendererTests.cs
@@ -88,6 +88,31 @@ public void ForwardedForHeaderContainsMultipleEntriesRenderFirstValue()
// Act
string result = renderer.Render(new LogEventInfo());
+ // Assert
+ Assert.Equal("127.0.0.1", result);
+ }
+
+ [Fact]
+ public void ForwardedForHeaderPresentWithCustomRenderForwardedValue()
+ {
+ // Arrange
+ var (renderer, httpContext) = CreateWithHttpContext();
+
+#if !ASP_NET_CORE
+ httpContext.Request.ServerVariables.Returns(new NameValueCollection {{"REMOTE_ADDR", "192.0.0.0"}});
+ httpContext.Request.Headers.Returns(
+ new NameValueCollection {{"header2", "127.0.0.1"}});
+#else
+ var headers = new HeaderDict();
+ headers.Add("header2", new StringValues("127.0.0.1"));
+ httpContext.Request.Headers.Returns(callinfo => headers);
+#endif
+ renderer.CheckForwardedForHeader = true;
+ renderer.ForwardedForHeader = "header2";
+
+ // Act
+ string result = renderer.Render(new LogEventInfo());
+
// Assert
Assert.Equal("127.0.0.1", result);
}
diff --git a/tests/Shared/LayoutRenderers/AspNetRequestUrlRendererTests.cs b/tests/Shared/LayoutRenderers/AspNetRequestUrlRendererTests.cs
index 6752d829..524f9daf 100644
--- a/tests/Shared/LayoutRenderers/AspNetRequestUrlRendererTests.cs
+++ b/tests/Shared/LayoutRenderers/AspNetRequestUrlRendererTests.cs
@@ -10,11 +10,15 @@
using Microsoft.Extensions.Primitives;
using HttpContextBase = Microsoft.AspNetCore.Http.HttpContext;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
#endif
using NLog.Web.LayoutRenderers;
using NSubstitute;
+
using Xunit;
+using System.IO;
+
namespace NLog.Web.Tests.LayoutRenderers
{
public class AspNetRequestUrlRendererTests : LayoutRenderersTestBase
@@ -146,8 +150,32 @@ public void UrlPresentRenderNonEmpty_ExcludeHost_IncludeQueryString()
Assert.Equal("http:///Test.asp?t=1", result);
}
+#if ASP_NET_CORE
+ [Fact]
+ public void UrlPresentRenderNonEmpty_UseRawTarget()
+ {
+ var renderer = CreateRenderer("www.google.com:80", "?t=1", "http", "/Test.asp", rawTarget: "/rawTarget");
+ renderer.UseRawTarget = true;
- private static AspNetRequestUrlRenderer CreateRenderer(string hostBase, string queryString = "", string scheme = "http", string page = "/", string pathBase = "")
+ string result = renderer.Render(LogEventInfo.CreateNullEvent());
+
+ Assert.Equal("http://www.google.com/rawTarget", result);
+ }
+
+ [Fact]
+ public void UrlPresentRenderNonEmpty_UseRawTarget_IncludeQueryString()
+ {
+ var renderer = CreateRenderer("www.google.com:80", "?t=1", "http", "/Test.asp", rawTarget: "/rawTarget");
+ renderer.UseRawTarget = true;
+ renderer.IncludeQueryString = true;
+
+ string result = renderer.Render(LogEventInfo.CreateNullEvent());
+
+ Assert.Equal("http://www.google.com/rawTarget", result);
+ }
+#endif
+
+ private static AspNetRequestUrlRenderer CreateRenderer(string hostBase, string queryString = "", string scheme = "http", string page = "/", string pathBase = "", string rawTarget = null)
{
var (renderer, httpContext) = CreateWithHttpContext();
@@ -160,8 +188,40 @@ private static AspNetRequestUrlRenderer CreateRenderer(string hostBase, string q
httpContext.Request.QueryString.Returns(new QueryString(queryString));
httpContext.Request.Host.Returns(new HostString(hostBase));
httpContext.Request.Scheme.Returns(scheme);
+
+ if (rawTarget != null)
+ {
+ httpContext.Request.HttpContext.Returns(httpContext);
+
+ var httpRequestFeature = new HttpRequestFeatureMock();
+ httpRequestFeature.RawTarget = rawTarget;
+ var collection = new FeatureCollection();
+ collection.Set(httpRequestFeature);
+ httpContext.Features.Returns(collection);
+ }
#endif
return renderer;
}
+
+#if ASP_NET_CORE
+
+ private class HttpRequestFeatureMock : IHttpRequestFeature
+ {
+ #region Implementation of IHttpRequestFeature
+
+ public Stream Body { get; set; }
+ public IHeaderDictionary Headers { get; set; }
+ public string Method { get; set; }
+ public string Path { get; set; }
+ public string PathBase { get; set; }
+ public string Protocol { get; set; }
+ public string QueryString { get; set; }
+ public string RawTarget { get; set; }
+ public string Scheme { get; set; }
+
+ #endregion Implementation of IHttpRequestFeature
+ }
+
+#endif
}
}