CSharpHttpServer/SimpleHttpServer/Types/BaseEndpointCheckAttribute.cs

118 lines
4.2 KiB
C#

using System.Net;
using System.Reflection;
namespace SimpleHttpServer.Types;
public abstract class InternalEndpointCheckAttribute : Attribute {
public InternalEndpointCheckAttribute() {
CheckSharedVariables();
}
private void CheckSharedVariables() {
foreach (var f in GetType().GetRuntimeFields()) {
if (f.FieldType.IsAssignableTo(typeof(SharedVariable))) {
if (!f.IsInitOnly) {
throw new Exception($"Found non-readonly global field {f}!");
}
if (f.GetValue(this) == null) {
throw new Exception("Global fields must be assigned in the CCTOR!");
}
}
}
}
private void Initialize(object? instance, Dictionary<FieldInfo, List<(InternalEndpointCheckAttribute, SharedVariable)>> globals) {
SetInstance(instance);
foreach (var f in GetType().GetRuntimeFields()) {
if (f.FieldType.IsAssignableTo(typeof(SharedVariable))) {
SharedVariable origVal = (SharedVariable) f.GetValue(this)!;
if (globals.TryGetValue(f, out var options)) {
bool foundMatch = false;
foreach ((var checker, var gv) in options) {
if (Match(checker)) {
foundMatch = true;
// we need to unify their global variables
f.SetValue(this, gv);
}
}
if (!foundMatch) {
options.Add((this, origVal));
}
} else {
globals.Add(f, new List<(InternalEndpointCheckAttribute, SharedVariable)>() { (this, origVal) });
}
}
}
}
public static void Initialize(object? instance, IEnumerable<InternalEndpointCheckAttribute> endPointChecks) {
Dictionary<FieldInfo, List<(InternalEndpointCheckAttribute, SharedVariable)>> globals = new();
foreach (var check in endPointChecks) {
check.Initialize(instance, globals);
}
}
private interface SharedVariable {
// Tagging interface
}
/// <summary>
/// Represents a Mutable Shared Variable. Fields of this type need to be initialized in the CCtor.
/// </summary>
protected sealed class MSV<V> : SharedVariable {
private readonly V __default;
public V Val { get; set; } = default!;
public MSV() : this(default!) { }
public MSV(V _default) {
__default = _default;
}
public static implicit operator V(MSV<V> v) => v.Val;
}
/// <summary>
/// Represents an Immutable Shared Variable. Fields of this type need to be initialized in the CCtor.
/// </summary>
protected sealed class ISV<V> : SharedVariable {
private readonly V __default;
public V Val { get; } = default!;
public ISV() : this(default!) { }
public ISV(V _default) {
__default = _default;
}
public static implicit operator V(ISV<V> v) => v.Val;
}
/// <summary>
/// Executed when the endpoint is invoked. The endpoint invocation is skipped if any of the checks fail.
/// </summary>
/// <returns>True to allow invocation, false to prevent.</returns>
public abstract bool Check(HttpListenerRequest req);
protected virtual bool Match(InternalEndpointCheckAttribute other) => true;
internal abstract void SetInstance(object? instance);
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public abstract class BaseEndpointCheckAttribute<T> : InternalEndpointCheckAttribute {
/// <summary>
/// A reference to the instance of the class that this attribute is attached to.
/// Will be null iff an class factory was passed in <see cref="HttpServer.RegisterEndpointsFromType{T}(Func{T}?)"/>.
/// </summary>
protected internal T? EndpointClassInstance { get; internal set; } = default;
public BaseEndpointCheckAttribute() : base() { }
internal override void SetInstance(object? instance) {
if (instance != null)
EndpointClassInstance = (T?) instance;
}
}