mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-27 22:56:45 -04:00
New: Use ASP.NET Core instead of Nancy
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ImpromptuInterface;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Matching;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Readarr.Http.Validation
|
||||
{
|
||||
public interface IDfaMatcherBuilder
|
||||
{
|
||||
void AddEndpoint(RouteEndpoint endpoint);
|
||||
object BuildDfaTree(bool includeLabel = false);
|
||||
}
|
||||
|
||||
// https://github.com/dotnet/aspnetcore/blob/cc3d47f5501cdfae3e5b5be509ef2c0fb8cca069/src/Http/Routing/src/Matching/DfaNode.cs
|
||||
public interface IDfaNode
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public List<Endpoint> Matches { get; }
|
||||
public IDictionary Literals { get; }
|
||||
public object Parameters { get; }
|
||||
public object CatchAll { get; }
|
||||
public IDictionary PolicyEdges { get; }
|
||||
}
|
||||
|
||||
public class DuplicateEndpointDetector
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public DuplicateEndpointDetector(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public Dictionary<string, List<string>> GetDuplicateEndpoints(EndpointDataSource dataSource)
|
||||
{
|
||||
// get the DfaMatcherBuilder - internal, so needs reflection :(
|
||||
var matcherBuilder = typeof(IEndpointSelectorPolicy).Assembly
|
||||
.GetType("Microsoft.AspNetCore.Routing.Matching.DfaMatcherBuilder");
|
||||
|
||||
var rawBuilder = _services.GetRequiredService(matcherBuilder);
|
||||
var builder = rawBuilder.ActLike<IDfaMatcherBuilder>();
|
||||
|
||||
var endpoints = dataSource.Endpoints;
|
||||
foreach (var t in endpoints)
|
||||
{
|
||||
if (t is RouteEndpoint endpoint && (endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching ?? false) == false)
|
||||
{
|
||||
builder.AddEndpoint(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign each node a sequential index.
|
||||
var visited = new Dictionary<IDfaNode, int>();
|
||||
var duplicates = new Dictionary<string, List<string>>();
|
||||
|
||||
var rawTree = builder.BuildDfaTree(includeLabel: true);
|
||||
|
||||
Visit(rawTree, LogDuplicates);
|
||||
|
||||
return duplicates;
|
||||
|
||||
void LogDuplicates(IDfaNode node)
|
||||
{
|
||||
if (!visited.TryGetValue(node, out var label))
|
||||
{
|
||||
label = visited.Count;
|
||||
visited.Add(node, label);
|
||||
}
|
||||
|
||||
// We can safely index into visited because this is a post-order traversal,
|
||||
// all of the children of this node are already in the dictionary.
|
||||
var filteredMatches = node?.Matches?.Where(x => !x.DisplayName.StartsWith("Readarr.Http.Frontend.StaticResourceController")).ToList();
|
||||
var matchCount = filteredMatches?.Count ?? 0;
|
||||
if (matchCount > 1)
|
||||
{
|
||||
var duplicateEndpoints = filteredMatches.Select(x => x.DisplayName).ToList();
|
||||
duplicates[node.Label] = duplicateEndpoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Visit(object rawNode, Action<IDfaNode> visitor)
|
||||
{
|
||||
var node = rawNode.ActLike<IDfaNode>();
|
||||
if (node.Literals?.Values != null)
|
||||
{
|
||||
foreach (var dictValue in node.Literals.Values)
|
||||
{
|
||||
Visit(dictValue, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
// Break cycles
|
||||
if (node.Parameters != null && !ReferenceEquals(rawNode, node.Parameters))
|
||||
{
|
||||
Visit(node.Parameters, visitor);
|
||||
}
|
||||
|
||||
// Break cycles
|
||||
if (node.CatchAll != null && !ReferenceEquals(rawNode, node.CatchAll))
|
||||
{
|
||||
Visit(node.CatchAll, visitor);
|
||||
}
|
||||
|
||||
if (node.PolicyEdges?.Values != null)
|
||||
{
|
||||
foreach (var dictValue in node.PolicyEdges.Values)
|
||||
{
|
||||
Visit(dictValue, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
visitor(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user