Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
lilith committed Jan 25, 2024
1 parent 56e41b4 commit d126cfe
Show file tree
Hide file tree
Showing 28 changed files with 509 additions and 100 deletions.
7 changes: 7 additions & 0 deletions examples/Imageflow.Server.ExampleMinimal/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
using Imazen.Abstractions.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Imageflow.Server.ExampleMinimal
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddImageflowReLogStoreAndReLoggerFactoryIfMissing();
services.AddLogging(builder => builder.AddConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Trace;
}));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Expand Down
13 changes: 2 additions & 11 deletions src/Imageflow.Server/ImageflowMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Imazen.Abstractions.DependencyInjection;
using Imazen.Abstractions.Logging;
using Imazen.Routing.Health;
using Imazen.Routing.Layers;
using Imazen.Routing.Serving;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -24,12 +23,6 @@ namespace Imageflow.Server
public class ImageflowMiddleware
{
private readonly RequestDelegate next;
private readonly IReLogger logger;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly IWebHostEnvironment env;

private readonly DiagnosticsPage diagnosticsPage;
private readonly Licensing licensing;
private readonly ImageflowMiddlewareOptions options;
private readonly GlobalInfoProvider globalInfoProvider;
private readonly IImageServer<RequestStreamAdapter,ResponseStreamAdapter, HttpContext> imageServer;
Expand All @@ -49,17 +42,15 @@ public ImageflowMiddleware(
IEnumerable<IBlobCacheProvider> blobCacheProviders,
ImageflowMiddlewareOptions options)
{

var retainedLogStore = retainedLogStores.FirstOrDefault() ?? new ReLogStore(new ReLogStoreOptions());
var loggerFactory = loggerFactories.FirstOrDefault() ?? new ReLoggerFactory(legacyLoggerFactory, retainedLogStore);
logger = loggerFactory.CreateReLogger("ImageflowMiddleware");
var logger = loggerFactory.CreateReLogger("ImageflowMiddleware");

this.next = next;
options.Licensing ??= new Licensing(LicenseManagerSingleton.GetOrCreateSingleton(
"imageflow_", new[] {env.ContentRootPath, Path.GetTempPath()}));
licensing = options.Licensing;
var licensing = options.Licensing;
this.options = options;
this.env = env;

var container = new ImageServerContainer(serviceProvider);
container.Register(env);
Expand Down
3 changes: 2 additions & 1 deletion src/Imageflow.Server/Internal/GlobalInfoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ public void Add(IInfoAccumulator query)
}
foreach (var p in everything.OfType<IInfoProvider>())
{
p?.Add(query);
if (p != this)
p?.Add(query);
}

}
Expand Down
10 changes: 6 additions & 4 deletions src/Imageflow.Server/Internal/MiddlewareOptionsServerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@ public void PopulateServices()
serverContainer.Register(watermarkingLogicOptions);


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

var routingEngine = router.Build(logger);

serverContainer.Register(routingEngine);

//TODO: Add a way to get the current ILicenseChecker
var imageServer = new ImageServer<RequestStreamAdapter,ResponseStreamAdapter, HttpContext>(
Expand Down Expand Up @@ -133,7 +135,7 @@ private PathPrefixHandler<Func<MutableRequestEventArgs, bool>> WrapUrlEventArgs(
return result;
});
}
private RoutingBuilder CreateRoutes(RoutingBuilder builder)
private RoutingBuilder CreateRoutes(RoutingBuilder builder, List<IPathMapping> mappedPaths)
{

// signature layer
Expand Down Expand Up @@ -200,9 +202,9 @@ private RoutingBuilder CreateRoutes(RoutingBuilder builder)
//builder.AddLayer(new LicensingLayer(options.Licensing, options.EnforcementMethod));


if (options.MappedPaths.Count > 0)
if (mappedPaths.Count > 0)
{
builder.AddLayer(new LocalFilesLayer(options.MappedPaths.Select(a =>
builder.AddLayer(new LocalFilesLayer(mappedPaths.Select(a =>
(IPathMapping)new PathMapping(a.VirtualPath, a.PhysicalPath, a.IgnorePrefixCase)).ToList()));
}

Expand Down
30 changes: 15 additions & 15 deletions src/Imageflow.Server/LegacyOptions/ImageflowMiddlewareOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ internal struct ExtensionlessPath

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

internal Licensing Licensing { get; set; }
internal Licensing? Licensing { get; set; }

public string MyOpenSourceProjectUrl { get; set; } = "https://i-need-a-license.com";

Expand All @@ -42,30 +42,30 @@ internal struct ExtensionlessPath

internal AccessDiagnosticsFrom DiagnosticsAccess { get; set; } = AccessDiagnosticsFrom.LocalHost;

internal string DiagnosticsPassword { get; set; }
internal string? DiagnosticsPassword { get; set; }

public bool UsePresetsExclusively { get; set; }

public string DefaultCacheControlString { get; set; } = "";

public string DefaultCacheControlString { get; set; }

public RequestSignatureOptions RequestSignatureOptions { get; set; }
public SecurityOptions JobSecurityOptions { get; set; }
public RequestSignatureOptions RequestSignatureOptions { get; set; } = RequestSignatureOptions.Empty;
public SecurityOptions JobSecurityOptions { get; set; } = new();

internal readonly List<PathPrefixHandler<Action<UrlEventArgs>>> Rewrite = new List<PathPrefixHandler<Action<UrlEventArgs>>>();
internal readonly List<PathPrefixHandler<Action<UrlEventArgs>>> Rewrite = [];

internal readonly List<PathPrefixHandler<Func<UrlEventArgs, bool>>> PreRewriteAuthorization = new List<PathPrefixHandler<Func<UrlEventArgs, bool>>>();
internal readonly List<PathPrefixHandler<Func<UrlEventArgs, bool>>> PreRewriteAuthorization = [];

internal readonly List<PathPrefixHandler<Func<UrlEventArgs, bool>>> PostRewriteAuthorization = new List<PathPrefixHandler<Func<UrlEventArgs, bool>>>();
internal readonly List<PathPrefixHandler<Func<UrlEventArgs, bool>>> PostRewriteAuthorization = [];

internal readonly List<PathPrefixHandler<Action<WatermarkingEventArgs>>> Watermarking = new List<PathPrefixHandler<Action<WatermarkingEventArgs>>>();
internal readonly List<PathPrefixHandler<Action<WatermarkingEventArgs>>> Watermarking = [];


internal readonly Dictionary<string, string> CommandDefaults = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
internal readonly Dictionary<string, string> CommandDefaults = new(StringComparer.OrdinalIgnoreCase);
public bool ApplyDefaultCommandsToQuerylessUrls { get; set; } = false;

internal readonly Dictionary<string, PresetOptions> Presets = new Dictionary<string, PresetOptions>(StringComparer.OrdinalIgnoreCase);
internal readonly Dictionary<string, PresetOptions> Presets = new(StringComparer.OrdinalIgnoreCase);

internal readonly List<ExtensionlessPath> ExtensionlessPaths = new List<ExtensionlessPath>();
internal readonly List<ExtensionlessPath> ExtensionlessPaths = [];
/// <summary>
/// Use this to add default command values if they are missing. Does not affect image requests with no querystring.
/// Example: AddCommandDefault("down.colorspace", "srgb") reverts to ImageResizer's legacy behavior in scaling shadows and highlights.
Expand Down Expand Up @@ -208,7 +208,7 @@ public ImageflowMiddlewareOptions MapPath(string virtualPath, string physicalPat

public ImageflowMiddlewareOptions AddWatermark(NamedWatermark watermark)
{
if (namedWatermarks.Any(w => w.Name.Equals(watermark.Name, StringComparison.OrdinalIgnoreCase)))
if (namedWatermarks.Any(w => w.Name?.Equals(watermark.Name, StringComparison.OrdinalIgnoreCase) ?? false))
{
throw new InvalidOperationException($"A watermark already exists by the name {watermark.Name}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class RequestSignatureOptions

internal List<SignaturePrefix> Prefixes { get; } = new List<SignaturePrefix>();
public bool IsEmpty => Prefixes.Count == 0 && DefaultSigningKeys.Count == 0 && DefaultRequirement == SignatureRequired.Never;

public static RequestSignatureOptions Empty => new RequestSignatureOptions(SignatureRequired.Never, new List<string>());

public RequestSignatureOptions(SignatureRequired defaultRequirement, IEnumerable<string> defaultSigningKeys)
{
Expand Down
2 changes: 2 additions & 0 deletions src/Imazen.Abstractions/Blobs/SearchableBlobTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ private SearchableBlobTag(string key, string value)
public string Key { get; init; }
public string Value { get; init; }

public KeyValuePair<string, string> ToKeyValuePair() => new(Key, Value);

public int EstimateAllocatedBytesRecursive =>
Key.EstimateMemorySize(true) + Value.EstimateMemorySize(true) + 8;

Expand Down
57 changes: 53 additions & 4 deletions src/Imazen.Abstractions/Concurrency/ConcurrencyHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class ConcurrencyHelpers
/// <param name="cancellationToken"></param>
/// <typeparam name="TRes"></typeparam>
/// <returns></returns>
public static Task<TRes?> WhenAnyMatchesOrDefault<TRes>(
public static Task<TRes?> WhenAnyMatchesOrDefaultLegacy<TRes>(
List<Task<TRes>> allTasks,
Func<TRes, bool> predicate,
CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -40,13 +40,62 @@ public static class ConcurrencyHelpers
taskCompletionSource.TrySetResult(default);
}
};

// run all simultaneously, and set the result when one completes and matches the predicate
foreach (var task in allTasks)
{
// I guess it get scheduled anyway?
task.ContinueWith(continuation, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
task.ContinueWith(continuation, cancellationToken, TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

return taskCompletionSource.Task;
}


/// <summary>
/// This alternate implementation uses Task.Any in a loop
/// </summary>
public static async Task<TRes?> WhenAnyMatchesOrDefault<TRes>(
List<Task<TRes>> allTasks,
Func<TRes, bool> predicate,
CancellationToken cancellationToken = default)
{
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}

var completedTasks = allTasks.Where(t => t.IsCompleted).ToList();
var match = completedTasks.FirstOrDefault(
t => t.Status == TaskStatus.RanToCompletion && predicate(t.Result));
if (match != null)
{
return match.Result;
}

// If all tasks are completed (faulted, cancelled, or success), and none match, return default
var incompleteTasks = allTasks.Where(t => !t.IsCompleted).ToList();
if (incompleteTasks.Count == 0)
{
return default;
}

// Wait for any task to complete
var taskComplete = await Task.WhenAny(incompleteTasks).ConfigureAwait(false);
// If it didn't succeed and meet criteria, try again
if (taskComplete.Status != TaskStatus.RanToCompletion)
continue;
var result = taskComplete.Result;
if (predicate(result))
{
return result;
}

}
return taskCompletionSource.Task;

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public interface IImageServerContainer : IServiceProvider
IServiceProvider? GetOuterProvider();

IEnumerable<T> GetInstanceOfEverythingLocal<T>();
bool Contains<T>();
}

public class ImageServerContainer : IImageServerContainer
Expand All @@ -36,6 +37,12 @@ public IEnumerable<T> GetInstanceOfEverythingLocal<T>()
{
return _services.SelectMany(kvp => kvp.Value.Where(v => v is T).Cast<T>());
}

public bool Contains<T>()
{
return _services.ContainsKey(typeof(T));
}

public void Register<TService>(Func<TService> instanceCreator) where TService : class
{
var instance = instanceCreator();
Expand Down
8 changes: 8 additions & 0 deletions src/Imazen.Abstractions/IUniqueNamed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ public interface IUniqueNamed

string UniqueName { get; }
}

public static class UniqueNamedExtensions
{
public static string NameAndClass<T>(this T obj) where T : IUniqueNamed
{
return $"{obj.UniqueName} ({obj.GetType().Name})";
}
}
}
4 changes: 2 additions & 2 deletions src/Imazen.Abstractions/Resulting/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ public static bool TryUnwrap<T>(this CodeResult<T> result, [MaybeNullWhen(false)

public static TE UnwrapError<T,TE>(this IResult<T,TE> result)
{
if (result.Value != null) throw new ResultIsOkException<T>(result.Value);
if (result.IsOk) throw new ResultIsOkException<T>(result.Value);
return result.Error!;
}

public static bool TryUnwrapError<T,TE>(this IResult<T,TE> result, [MaybeNullWhen(false)] out TE error)
{
if (result.Value != null)
if (result.IsOk)
{
error = default;
return false;
Expand Down
2 changes: 2 additions & 0 deletions src/Imazen.Abstractions/Resulting/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ protected Result(bool isOk, T? okValue, TE? errorValue)
Value = okValue;
IsOk = isOk;
Error = errorValue;
if (IsOk && Value == null) throw new ArgumentNullException(nameof(okValue));
if (!IsOk && Error == null) throw new ArgumentNullException(nameof(errorValue));
}
protected Result(T okValue) : this(true, okValue, default(TE))
{
Expand Down
38 changes: 26 additions & 12 deletions src/Imazen.Abstractions/Support/MemoryUsageEstimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ public interface IEstimateAllocatedBytesRecursive
int EstimateAllocatedBytesRecursive { get; }
}

public static class MemoryUsageEstimation
internal static class MemoryUsageEstimation
{
public static int EstimateMemorySize<T>(this T[]? obj, bool includeReference) where T : class
{
return (includeReference ? 8 : 0) + (obj == null ? 0 :
(24 + (obj.Length > 0 ? obj.Length * 8: 0)));
}


public static int EstimateMemorySize<T>(this List<T>? obj, bool includeReference) where T: IEstimateAllocatedBytesRecursive
{
return (includeReference ? 8 : 0) + (obj == null
Expand All @@ -35,21 +30,40 @@ public static int EstimateMemorySize<T>(this IReadOnlyCollection<T>? obj, bool i
+ obj.Sum(x => x.EstimateAllocatedBytesRecursive)));
}

public static int EstimateMemorySize<T>(this T obj) where T : unmanaged
{
return Marshal.SizeOf<T>();
}

public static int EstimateMemorySize<T>(this T? obj, bool includeReference) where T : unmanaged
/// <summary>
/// Can crash on structs, should only be used on primitive numeric types
/// </summary>
/// <param name="obj"></param>
/// <param name="includeReference"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static int EstimateMemorySize<T>(this T? obj, bool includeReference) where T : unmanaged
{
return Marshal.SizeOf<T>() + 1 + (includeReference ? 8 : 0);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="includeReference"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static int EstimateMemorySize<T>(this T obj, bool includeReference) where T : unmanaged
{
return Marshal.SizeOf<T>() + 1 + (includeReference ? 8 : 0);
}


public static int EstimateMemorySize(this DateTimeOffset obj, bool includeReference)
{
return 8 + 8 + (includeReference ? 8 : 0);
}
public static int EstimateMemorySize(this DateTimeOffset? obj, bool includeReference)
{
return 1 + 8 + 8 + (includeReference ? 8 : 0);
}

public static int EstimateMemorySize(this string? s, bool includeReference)
{
return (includeReference ? 8 : 0) + (s == null ? 0 : (s.Length + 1) * sizeof(char) + 8 + 4);
Expand Down
Loading

0 comments on commit d126cfe

Please sign in to comment.