Skip to content

Commit a80cc6d

Browse files
authored
Merge pull request #152 from Geta/feature/ignore-suggestions-regex
Feature/ignore suggestions regex
2 parents f2ca161 + 957c3a8 commit a80cc6d

File tree

6 files changed

+109
-9
lines changed

6 files changed

+109
-9
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ By default, requests to files with the following extensions will be ignored by t
159159

160160
If you want to specify this yourself, add `IgnoredResourceExtensions` to the configuration.
161161

162+
### Specifying ignored URLs
163+
164+
If certain URLs should be ignored, you can use the `IgnoreSuggestionsUrlRegexPattern` option:
165+
166+
```
167+
services.AddNotFoundHandler(o =>
168+
{
169+
o.IgnoreSuggestionsUrlRegexPattern = @"^(https?:\/\/[^\/]+)?\/(api|episerverapi|globalassets|siteassets)";
170+
});
171+
```
172+
173+
When a URL matches the specified regex pattern, suggestions will be skipped.
174+
175+
162176
## Restricting access to the Admin UI
163177

164178
By default, only users of `Administrators` role can access Admin UI. But you can configure your authorization policy when registering the NotFound handler.

src/Geta.NotFoundHandler/Core/RequestHandler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,11 @@ public virtual bool HandleRequest(Uri referer, Uri urlNotFound, out CustomRedire
154154
}
155155
else
156156
{
157-
// log request to database - if logging is turned on.
158-
if (_configuration.Logging == LoggerMode.On)
157+
// Safe logging
158+
var logUrl = _configuration.LogWithHostname ? urlNotFound.ToString() : urlNotFound.PathAndQuery;
159+
160+
if (_requestLogger.ShouldLogRequest(logUrl))
159161
{
160-
// Safe logging
161-
var logUrl = _configuration.LogWithHostname ? urlNotFound.ToString() : urlNotFound.PathAndQuery;
162162
_requestLogger.LogRequest(logUrl, referer?.ToString());
163163
}
164164
}

src/Geta.NotFoundHandler/Core/Suggestions/IRequestLogger.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ namespace Geta.NotFoundHandler.Core.Suggestions
66
public interface IRequestLogger
77
{
88
void LogRequest(string oldUrl, string referer);
9+
bool ShouldLogRequest(string oldUrl);
910
}
10-
}
11+
}

src/Geta.NotFoundHandler/Core/Suggestions/RequestLogger.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Concurrent;
66
using System.Linq;
7+
using System.Text.RegularExpressions;
78
using Geta.NotFoundHandler.Data;
89
using Geta.NotFoundHandler.Infrastructure.Configuration;
910
using Microsoft.Extensions.Logging;
@@ -27,7 +28,7 @@ public RequestLogger(
2728
_configuration = options.Value;
2829
}
2930

30-
public void LogRequest(string oldUrl, string referer)
31+
public virtual void LogRequest(string oldUrl, string referer)
3132
{
3233
var bufferSize = _configuration.BufferSize;
3334
if (!LogQueue.IsEmpty && LogQueue.Count >= bufferSize)
@@ -80,6 +81,36 @@ private void LogRequests(ConcurrentQueue<LogEvent> logEvents)
8081
}
8182
}
8283

84+
public virtual bool ShouldLogRequest(string url)
85+
{
86+
// log request to database - if logging is turned on.
87+
if (_configuration.Logging == LoggerMode.Off)
88+
{
89+
return false;
90+
}
91+
92+
var ignorePattern = _configuration.IgnoreSuggestionsUrlRegexPattern;
93+
if (string.IsNullOrWhiteSpace(ignorePattern))
94+
{
95+
return true;
96+
}
97+
98+
try
99+
{
100+
return !Regex.IsMatch(url, ignorePattern, RegexOptions.None, TimeSpan.FromMilliseconds(50));
101+
}
102+
catch (RegexMatchTimeoutException ex)
103+
{
104+
_logger.LogWarning(ex, "Regex matching timed out for pattern: {Pattern} and URL: {Url}", ignorePattern, url);
105+
return true;
106+
}
107+
catch (Exception ex)
108+
{
109+
_logger.LogWarning(ex, "Unexpected error while matching regex for URL: {Url} and pattern: {Pattern}", url, ignorePattern);
110+
return true;
111+
}
112+
}
113+
83114
private static ConcurrentQueue<LogEvent> LogQueue { get; } = new ConcurrentQueue<LogEvent>();
84115
}
85116
}

src/Geta.NotFoundHandler/Infrastructure/Configuration/NotFoundHandlerOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class NotFoundHandlerOptions
2626
{"jpg", "gif", "png", "css", "js", "ico", "swf", "woff"};
2727

2828
public LoggerMode Logging { get; set; } = LoggerMode.On;
29+
public string IgnoreSuggestionsUrlRegexPattern { get; set; }
2930
public bool LogWithHostname { get; set; } = false;
3031
public string ConnectionString { get; private set; }
3132
public string BootstrapJsUrl { get; set; } = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js";

tests/Geta.NotFoundHandler.Tests/RequestHandlerTests.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Geta.NotFoundHandler.Core;
55
using Geta.NotFoundHandler.Core.Redirects;
66
using Geta.NotFoundHandler.Core.Suggestions;
7+
using Geta.NotFoundHandler.Data;
78
using Geta.NotFoundHandler.Infrastructure.Configuration;
89
using Geta.NotFoundHandler.Tests.Base;
910
using Microsoft.AspNetCore.Http;
@@ -23,16 +24,23 @@ public class RequestHandlerTests
2324
private static readonly Uri DefaultOldUri = new Uri("http://example.com/old");
2425
private readonly NotFoundHandlerOptions _configuration;
2526

27+
private const string IgnoreSuggestionsUrlRegexPattern = @"^(https?:\/\/[^\/]+)?\/(api|nilleapi|episerverapi|globalassets|siteassets)(?!\/private)(\/.*)?$";
28+
2629
public RequestHandlerTests()
2730
{
2831
_redirectHandler = A.Fake<IRedirectHandler>();
29-
_requestLogger = A.Fake<IRequestLogger>();
3032
var options = A.Fake<IOptions<NotFoundHandlerOptions>>();
3133
_configuration = new NotFoundHandlerOptions();
3234
A.CallTo(() => options.Value).Returns(_configuration);
33-
var logger = A.Fake<ILogger<RequestHandler>>();
35+
var suggestionsRepository = A.Fake<ISuggestionRepository>();
36+
var requestLoggerLogger = A.Fake<ILogger<RequestLogger>>();
37+
38+
_requestLogger = A.Fake<RequestLogger>(o => o.WithArgumentsForConstructor(new object[] {options, requestLoggerLogger, suggestionsRepository })
39+
.CallsBaseMethods());
40+
41+
var requestHandlerLogger = A.Fake<ILogger<RequestHandler>>();
3442
_sut = A.Fake<RequestHandler>(
35-
o => o.WithArgumentsForConstructor(new object[] { _redirectHandler, _requestLogger, options, logger })
43+
o => o.WithArgumentsForConstructor(new object[] { _redirectHandler, _requestLogger, options, requestHandlerLogger })
3644
.CallsBaseMethods());
3745

3846
_httpContext = new DefaultHttpContext();
@@ -255,6 +263,51 @@ private void AssertRequestLogged(Uri referrer, Uri urlNotFound)
255263
.MustHaveHappened();
256264
}
257265

266+
[Theory]
267+
[InlineData("https://example.com/api/something.json")]
268+
[InlineData("/episerverapi/content")]
269+
[InlineData("/globalassets/image.jpg")]
270+
[InlineData("/siteassets/style.css")]
271+
[InlineData("/api/resource")]
272+
public void HandleRequest_ignore_suggestions_when_regex_matches(string url)
273+
{
274+
WhenLoggingIsOn();
275+
276+
_configuration.IgnoreSuggestionsUrlRegexPattern = IgnoreSuggestionsUrlRegexPattern;
277+
278+
var result = _requestLogger.ShouldLogRequest(url);
279+
280+
Assert.False(result);
281+
}
282+
283+
[Theory]
284+
[InlineData("https://example.com/home")]
285+
[InlineData("/")]
286+
[InlineData("/login")]
287+
public void HandleRequest_do_not_ignore_suggestions_when_regex_do_not_matches(string url)
288+
{
289+
WhenLoggingIsOn();
290+
291+
_configuration.IgnoreSuggestionsUrlRegexPattern = IgnoreSuggestionsUrlRegexPattern;
292+
293+
var result = _requestLogger.ShouldLogRequest(url);
294+
295+
Assert.True(result);
296+
}
297+
298+
[Theory]
299+
[InlineData("/siteassets/private/file.json")]
300+
public void HandleRequest_do_not_ignore_suggestions_when_regex_matches_using_lookahead(string url)
301+
{
302+
WhenLoggingIsOn();
303+
304+
_configuration.IgnoreSuggestionsUrlRegexPattern = IgnoreSuggestionsUrlRegexPattern;
305+
306+
var result = _requestLogger.ShouldLogRequest(url);
307+
308+
Assert.True(result);
309+
}
310+
258311
private void WhenLoggingIsOn()
259312
{
260313
_configuration.Logging = LoggerMode.On;

0 commit comments

Comments
 (0)