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> 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 endPointChecks) { Dictionary> globals = new(); foreach (var check in endPointChecks) { check.Initialize(instance, globals); } } private interface SharedVariable { // Tagging interface } /// /// Represents a Mutable Shared Variable. Fields of this type need to be initialized in the CCtor. /// protected sealed class MSV : 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.Val; } /// /// Represents an Immutable Shared Variable. Fields of this type need to be initialized in the CCtor. /// protected sealed class ISV : 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.Val; } /// /// Executed when the endpoint is invoked. The endpoint invocation is skipped if any of the checks fail. /// /// True to allow invocation, false to prevent. 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 : InternalEndpointCheckAttribute { /// /// A reference to the instance of the class that this attribute is attached to. /// Will be null iff an class factory was passed in . /// protected internal T? EndpointClassInstance { get; internal set; } = default; public BaseEndpointCheckAttribute() : base() { } internal override void SetInstance(object? instance) { if (instance != null) EndpointClassInstance = (T?) instance; } }