Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
lilith committed Jan 26, 2024
1 parent 375613e commit d78d297
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 121 deletions.
19 changes: 10 additions & 9 deletions src/Imageflow.Server/Internal/MiddlewareOptionsServerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ public void PopulateServices()
return list;
});
serverContainer.Register(watermarkingLogicOptions);


var router = CreateRoutes(new RoutingBuilder(),mappedPaths);

var routingBuilder = new RoutingBuilder();
routingBuilder.SetGlobalPreconditionsToRequireImageExtensionOrExtensionlessPathPrefixes(options.ExtensionlessPaths);
var router = CreateRoutes(routingBuilder,mappedPaths);

var routingEngine = router.Build(logger);

Expand Down Expand Up @@ -138,7 +139,7 @@ public void PopulateServices()
return result;
});
}
private RoutingBuilder CreateRoutes(RoutingBuilder builder, List<IPathMapping> mappedPaths)
private RoutingBuilder CreateRoutes(RoutingBuilder builder, IReadOnlyCollection<IPathMapping> mappedPaths)
{

// signature layer
Expand Down Expand Up @@ -185,6 +186,7 @@ private RoutingBuilder CreateRoutes(RoutingBuilder builder, List<IPathMapping> m
}

// Apply command defaults
// TODO: only to already processing images?
if (options.CommandDefaults.Count > 0)
{
builder.AddLayer(new CommandDefaultsLayer(new CommandDefaultsLayerOptions()
Expand Down Expand Up @@ -220,11 +222,10 @@ private RoutingBuilder CreateRoutes(RoutingBuilder builder, List<IPathMapping> m
builder.AddLayer(new BlobProvidersLayer(blobProviders, blobWrapperProviders));
}

builder.AddLayer(diagnosticsPage);
builder.AddGlobalLayer(diagnosticsPage);


builder.SetDefaultPreconditionsToRequireImageExtension()
.AddEndpoint(Conditions.HasPathSuffix("/imageflow.ready"),
builder.AddGlobalEndpoint(Conditions.HasPathSuffix("/imageflow.ready"),
(_) =>
{
options.Licensing?.FireHeartbeat();
Expand All @@ -234,14 +235,14 @@ private RoutingBuilder CreateRoutes(RoutingBuilder builder, List<IPathMapping> m
}
});

builder.AddEndpoint(Conditions.HasPathSuffix("/imageflow.health"),
builder.AddGlobalEndpoint(Conditions.HasPathSuffix("/imageflow.health"),
(_) =>
{
options.Licensing?.FireHeartbeat();
return SmallHttpResponse.NoStore(200, "Imageflow.Server is healthy.");
});

builder.AddEndpoint(Conditions.HasPathSuffix("/imageflow.license"),
builder.AddGlobalEndpoint(Conditions.HasPathSuffix("/imageflow.license"),
(req) =>
{
options.Licensing?.FireHeartbeat();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ namespace Imageflow.Server
{
public class ImageflowMiddlewareOptions: IInfoProvider
{
internal struct ExtensionlessPath
{
internal string Prefix { get; set; }
internal record struct ExtensionlessPath(string StringToCompare, StringComparison StringComparison)
: IStringAndComparison;

internal StringComparison PrefixComparison { get; set; }
}
internal string? LicenseKey { get; set; }
internal EnforceLicenseWith EnforcementMethod { get; set; } = EnforceLicenseWith.Http422Error;

Expand Down Expand Up @@ -90,7 +87,7 @@ public ImageflowMiddlewareOptions AddPreset(PresetOptions preset)

public ImageflowMiddlewareOptions HandleExtensionlessRequestsUnder(string prefix, StringComparison prefixComparison = StringComparison.Ordinal)
{
ExtensionlessPaths.Add(new ExtensionlessPath() { Prefix = prefix, PrefixComparison = prefixComparison});
ExtensionlessPaths.Add(new ExtensionlessPath() { StringToCompare = prefix, StringComparison = prefixComparison});
return this;
}

Expand Down
8 changes: 8 additions & 0 deletions src/Imageflow.Server/LegacyOptions/PathMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,13 @@ public PathMapping(string virtualPath, string physicalPath, bool ignorePrefixCas
public string VirtualPath { get; }
public string PhysicalPath { get; }
public bool IgnorePrefixCase { get; }
/// <summary>
/// Duplicate of VirtualPath for IStringAndComparison
/// </summary>
public string StringToCompare => VirtualPath;
/// <summary>
/// If IgnorePrefixCase is true, returns OrdinalIgnoreCase, otherwise Ordinal
/// </summary>
public StringComparison StringComparison => IgnorePrefixCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
}
}

This file was deleted.

9 changes: 9 additions & 0 deletions src/Imazen.Routing/Engine/ExtensionlessPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Imazen.Routing.Layers;

namespace Imazen.Routing.Engine;

public readonly record struct ExtensionlessPath(string Prefix, StringComparison StringComparison = StringComparison.OrdinalIgnoreCase)
: IStringAndComparison
{
public string StringToCompare => Prefix;
}
73 changes: 59 additions & 14 deletions src/Imazen.Routing/Engine/RoutingBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@ namespace Imazen.Routing.Engine;

public class RoutingBuilder
{

protected IFastCond? DefaultPreconditions { get; set; }
protected IFastCond? GlobalPreconditions { get; set; }
protected List<IRoutingLayer> Layers { get; } = new List<IRoutingLayer>();
// set default conditions (IFastCond)
// add page endpoints (IRoutingEndpoint)

// Add endpoints based on suffixes or exact matches. At build time, we roll these up into a single FastCond that can be evaluated quickly.

/// <summary>
/// Adds an endpoint that also extends GlobalPreconditions
/// </summary>
/// <param name="fastMatcher"></param>
/// <param name="endpoint"></param>
/// <returns></returns>
public RoutingBuilder AddGlobalEndpoint(IFastCond fastMatcher, IRoutingEndpoint endpoint)
=> AddEndpoint(true, fastMatcher, endpoint);

public RoutingBuilder AddEndpoint(IFastCond fastMatcher, IRoutingEndpoint endpoint)
=> AddEndpoint(false, fastMatcher, endpoint);

private RoutingBuilder AddEndpoint(bool global, IFastCond fastMatcher, IRoutingEndpoint endpoint)
{
if (global) AddAlternateGlobalPrecondition(fastMatcher);
Layers.Add(new SimpleLayer(fastMatcher.ToString() ?? "(error describing route)",
(request) => fastMatcher.Matches(request.MutablePath, request.ReadOnlyQueryWrapper) ?
CodeResult<IRoutingEndpoint>.Ok(endpoint) : null, fastMatcher));
Expand All @@ -28,21 +40,41 @@ public RoutingBuilder AddLayer(IRoutingLayer layer)
Layers.Add(layer);
return this;
}

public RoutingBuilder AddGlobalLayer(IRoutingLayer layer)
{
AddAlternateGlobalPrecondition(layer.FastPreconditions ?? Conditions.True);
Layers.Add(layer);
return this;
}
//Add predefined reusable responses

public RoutingBuilder AddEndpoint(IFastCond fastMatcher, IAdaptableReusableHttpResponse response)
/// <summary>
/// Adds an endpoint that also extends GlobalPreconditions
/// </summary>
/// <param name="fastMatcher"></param>
/// <param name="response"></param>
/// <returns></returns>
public RoutingBuilder AddGlobalEndpoint(IFastCond fastMatcher, IAdaptableReusableHttpResponse response)
{
var endpoint = new PredefinedResponseEndpoint(response);
return AddEndpoint(fastMatcher, endpoint);
return AddGlobalEndpoint(fastMatcher, endpoint);
}

public RoutingBuilder AddEndpoint(IFastCond fastMatcher, SmallHttpResponse response)
public RoutingBuilder AddEndpoint(IFastCond fastMatcher, IAdaptableReusableHttpResponse response)
{
var endpoint = new PredefinedResponseEndpoint(response);
return AddEndpoint(fastMatcher, endpoint);
}
// now with a delegate
/// <summary>
/// Adds an endpoint that also extends GlobalPreconditions
/// </summary>
/// <param name="fastMatcher"></param>
/// <param name="handler"></param>
/// <returns></returns>
public RoutingBuilder AddGlobalEndpoint(IFastCond fastMatcher, Func<IRequestSnapshot, IAdaptableHttpResponse> handler)
{
var endpoint = new SyncEndpointFunc(handler);
return AddGlobalEndpoint(fastMatcher, endpoint);
}

public RoutingBuilder AddEndpoint(IFastCond fastMatcher, Func<IRequestSnapshot, IAdaptableHttpResponse> handler)
{
Expand All @@ -53,21 +85,34 @@ public RoutingBuilder AddEndpoint(IFastCond fastMatcher, Func<IRequestSnapshot,
// Set default Preconditions IFastCond


public RoutingBuilder SetDefaultPreconditions(IFastCond fastMatcher)
public RoutingBuilder SetGlobalPreconditions(IFastCond fastMatcher)
{
DefaultPreconditions = fastMatcher;
GlobalPreconditions = fastMatcher;
return this;
}
public RoutingBuilder SetDefaultPreconditionsToRequireImageExtension()
public RoutingBuilder SetGlobalPreconditionsToRequireImageExtensionOrExtensionlessPathPrefixes<T>(IReadOnlyCollection<T>? extensionlessPaths = null)
where T : IStringAndComparison
{
DefaultPreconditions = Conditions.HasSupportedImageExtension;
if (extensionlessPaths is { Count: > 0 })
{
GlobalPreconditions = Conditions.HasSupportedImageExtension.Or(
Conditions.PathHasNoExtension.And(Conditions.HasPathPrefix(extensionlessPaths)));
return this;
}
GlobalPreconditions = Conditions.HasSupportedImageExtension;
return this;
}
public RoutingBuilder AddAlternateGlobalPrecondition(IFastCond? fastMatcher)
{
if (fastMatcher == null) return this;
if (fastMatcher is Conditions.FastCondFalse) return this;
GlobalPreconditions = GlobalPreconditions == null ? fastMatcher : GlobalPreconditions.Or(fastMatcher);
return this;
}


public RoutingEngine Build(IReLogger logger)
{
return new RoutingEngine(Layers.ToArray(), DefaultPreconditions ?? Conditions.True, logger);
return new RoutingEngine(Layers.ToArray(), GlobalPreconditions ?? Conditions.True, logger);
}


Expand Down
12 changes: 6 additions & 6 deletions src/Imazen.Routing/Engine/RoutingEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ public class RoutingEngine : IBlobRequestRouter, IHasDiagnosticPageSection
private readonly IRoutingLayer[] layers;
private readonly IReLogger logger;

private readonly Conditions.FastCondAnyOptimized mightHandleConditions;
internal RoutingEngine(IRoutingLayer[] layers, IFastCond fastCondLogic, IReLogger logger)
private readonly IFastCond mightHandleConditions;
internal RoutingEngine(IRoutingLayer[] layers, IFastCond globalPrecondition, IReLogger logger)
{
this.layers = layers;
this.logger = logger;

// Build a list of unique fast exits we must consult. We can skip duplicates.
var uniqueFastExits = new List<IFastCond>(4) { fastCondLogic };
var preconditions = new List<IFastCond>(layers.Length) { };
foreach (var layer in layers)
{
if (layer.FastPreconditions != null && !uniqueFastExits.Contains(layer.FastPreconditions))
if (layer.FastPreconditions != null && !preconditions.Contains(layer.FastPreconditions))
{
uniqueFastExits.Add(layer.FastPreconditions);
preconditions.Add(layer.FastPreconditions);
}
}
mightHandleConditions = new Conditions.FastCondAnyOptimized(uniqueFastExits);
mightHandleConditions = globalPrecondition.And(preconditions.AnyPrecondition()).Optimize();
}

public bool MightHandleRequest<TQ>(string path, TQ query) where TQ : IReadOnlyQueryWrapper
Expand Down
7 changes: 6 additions & 1 deletion src/Imazen.Routing/Helpers/PathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public static class PathHelpers

internal static bool IsImagePath(string path)
{
return Suffixes.Any(suffix => path.EndsWith(suffix, StringComparison.OrdinalIgnoreCase));
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var suffix in Suffixes)
{
if (path.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}

public static string? SanitizeImageExtension(string extension)
Expand Down
Loading

0 comments on commit d78d297

Please sign in to comment.