Rate Policy

This commit is contained in:
2026-07-13 17:15:00 +03:00
parent 31bda58460
commit 4f6d2d0801
46 changed files with 3142 additions and 468 deletions
@@ -38,6 +38,8 @@ namespace Esiur.Resource;
AttributeTargets.Field,
AllowMultiple = false,
Inherited = true)]
public sealed class AutoDeliveredAttribute : Attribute
public class AutoDeliveryAttribute : Attribute
{
}
}
@@ -0,0 +1,163 @@
using Esiur.Data.Types;
using System;
namespace Esiur.Resource;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface |
AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property |
AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter,
AllowMultiple = false, Inherited = true)]
public sealed class DescriptionAttribute : Attribute
{
public string Value { get; }
public DescriptionAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface |
AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property |
AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter,
AllowMultiple = true, Inherited = true)]
public sealed class ExampleAttribute : Attribute
{
public object Value { get; }
public ExampleAttribute(object value) => Value = value;
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Interface | AttributeTargets.Enum,
AllowMultiple = false, Inherited = true)]
public sealed class CategoryAttribute : Attribute
{
public string Value { get; }
public CategoryAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Interface | AttributeTargets.Enum,
AllowMultiple = false, Inherited = true)]
public sealed class SinceAttribute : Attribute
{
public string Value { get; }
public SinceAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property |
AttributeTargets.Field | AttributeTargets.Event,
AllowMultiple = false, Inherited = true)]
public sealed class TagsAttribute : Attribute
{
public string[] Values { get; }
public TagsAttribute(params string[] values) => Values = values;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Method | AttributeTargets.Event,
AllowMultiple = false, Inherited = true)]
public sealed class UnitAttribute : Attribute
{
public string Value { get; }
public UnitAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Parameter,
AllowMultiple = false, Inherited = true)]
public sealed class MinimumAttribute : Attribute
{
public object Value { get; }
public MinimumAttribute(object value) => Value = value;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Parameter,
AllowMultiple = false, Inherited = true)]
public sealed class MaximumAttribute : Attribute
{
public object Value { get; }
public MaximumAttribute(object value) => Value = value;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Parameter,
AllowMultiple = true, Inherited = true)]
public sealed class AllowedValueAttribute : Attribute
{
public object Value { get; }
public AllowedValueAttribute(object value) => Value = value;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Parameter,
AllowMultiple = false, Inherited = true)]
public sealed class PatternAttribute : Attribute
{
public string Value { get; }
public PatternAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Parameter,
AllowMultiple = false, Inherited = true)]
public sealed class FormatAttribute : Attribute
{
public string Value { get; }
public FormatAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method,
AllowMultiple = true, Inherited = true)]
public sealed class PreconditionAttribute : Attribute
{
public string Value { get; }
public PreconditionAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method,
AllowMultiple = true, Inherited = true)]
public sealed class PostconditionAttribute : Attribute
{
public string Value { get; }
public PostconditionAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method,
AllowMultiple = false, Inherited = true)]
public sealed class EffectsAttribute : Attribute
{
public OperationEffects Value { get; }
public EffectsAttribute(OperationEffects value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property |
AttributeTargets.Field | AttributeTargets.Event,
AllowMultiple = true, Inherited = true)]
public sealed class WarningAttribute : Attribute
{
public string Value { get; }
public WarningAttribute(string value) => Value = value;
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property |
AttributeTargets.Field | AttributeTargets.Event,
AllowMultiple = false, Inherited = true)]
public sealed class RelatedMembersAttribute : Attribute
{
public byte[] Indexes { get; }
public RelatedMembersAttribute(params byte[] indexes) => Indexes = indexes;
}
@@ -5,12 +5,13 @@ using System.Text;
namespace Esiur.Resource
{
/// <summary>
/// Indicates that previous values of an exported property are retained
/// Indicates that previous property values or event occurrences are retained
/// and may be fetched remotely.
/// </summary>
[AttributeUsage(
AttributeTargets.Property |
AttributeTargets.Field,
AttributeTargets.Field |
AttributeTargets.Event,
AllowMultiple = false,
Inherited = true)]
public sealed class HistoricalAttribute : Attribute
@@ -0,0 +1,26 @@
using System;
namespace Esiur.Resource;
/// <summary>
/// Applies a named Warehouse rate policy to an exported function or property setter.
/// </summary>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Property,
AllowMultiple = false,
Inherited = true)]
public sealed class RateControlAttribute : Attribute
{
/// <summary>
/// Gets the name used to resolve the policy from the owning Warehouse.
/// </summary>
public string PolicyName { get; }
public RateControlAttribute(string policyName)
{
if (string.IsNullOrWhiteSpace(policyName))
throw new ArgumentException("A rate policy name is required.", nameof(policyName));
PolicyName = policyName;
}
}
@@ -6,7 +6,9 @@ using System.Text;
namespace Esiur.Resource
{
/// <summary>
/// Marks an exported function as streaming and specifies its delivery mode.
/// Marks an AsyncReply-returning exported function as streaming and specifies
/// its delivery mode. IEnumerable and IAsyncEnumerable return types infer push
/// and pull mode respectively and do not require this attribute.
/// </summary>
[AttributeUsage(
AttributeTargets.Method,
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Resource.Attributes
namespace Esiur.Resource
{
[AttributeUsage(
AttributeTargets.Class |
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Resource.Attributes
namespace Esiur.Resource
{
/// <summary>
/// Indicates that an exported property has volatile synchronization
+50 -1
View File
@@ -31,6 +31,7 @@ using Esiur.Protocol;
using Esiur.Proxy;
using Esiur.Security.Authority;
using Esiur.Security.Permissions;
using Esiur.Security.RateLimiting;
using Org.BouncyCastle.Asn1.Cms;
using System;
using System.Collections;
@@ -88,6 +89,8 @@ public class Warehouse
Map<string, IAuthenticationProvider> _authenticationProviders = new Map<string, IAuthenticationProvider>();
List<IPermissionsManager> _permissionsManagers = new List<IPermissionsManager>();
readonly ConcurrentDictionary<string, RatePolicy> _ratePolicies
= new ConcurrentDictionary<string, RatePolicy>(StringComparer.Ordinal);
object _typeDefsLock = new object();
@@ -102,6 +105,11 @@ public class Warehouse
public KeyList<string, ProtocolInstance> Protocols { get; } = new KeyList<string, ProtocolInstance>();
/// <summary>
/// Runtime settings for this Warehouse.
/// </summary>
public WarehouseConfiguration Configuration { get; }
private Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
@@ -121,6 +129,41 @@ public class Warehouse
_permissionsManagers.Add(manager);
}
/// <summary>
/// Registers a named rate policy referenced by RateControl attributes.
/// </summary>
public void AddRatePolicy(RatePolicy policy)
{
if (policy == null)
throw new ArgumentNullException(nameof(policy));
if (string.IsNullOrWhiteSpace(policy.Name))
throw new ArgumentException("The rate policy must have a name.", nameof(policy));
if (!_ratePolicies.TryAdd(policy.Name, policy))
throw new InvalidOperationException($"A rate policy named `{policy.Name}` is already registered.");
}
/// <summary>
/// Registers a policy under the supplied name.
/// </summary>
public void AddRatePolicy(string name, RatePolicy policy)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("A rate policy name is required.", nameof(name));
if (policy == null)
throw new ArgumentNullException(nameof(policy));
policy.Name = name;
AddRatePolicy(policy);
}
public RatePolicy? TryGetRatePolicy(string name)
=> !string.IsNullOrWhiteSpace(name) && _ratePolicies.TryGetValue(name, out var policy)
? policy
: null;
public bool RemoveRatePolicy(string name)
=> !string.IsNullOrWhiteSpace(name) && _ratePolicies.TryRemove(name, out _);
public IAuthenticationProvider GetAuthenticationProvider(string name)
{
if (_authenticationProviders.ContainsKey(name))
@@ -137,8 +180,14 @@ public class Warehouse
}
public Warehouse()
public Warehouse() : this(new WarehouseConfiguration())
{
}
public Warehouse(WarehouseConfiguration configuration)
{
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
Protocols.Add("EP",
async (name, context)
=> await New<EpConnection>(name, context));
@@ -0,0 +1,33 @@
using System;
namespace Esiur.Resource;
/// <summary>
/// Runtime configuration owned by a Warehouse instance.
/// </summary>
public sealed class WarehouseConfiguration
{
public RateControlConfiguration RateControl { get; set; } = new RateControlConfiguration();
}
/// <summary>
/// Configures how repeated rate-control denials affect an EP connection.
/// </summary>
public sealed class RateControlConfiguration
{
/// <summary>
/// Number of denials within <see cref="DenialWindow"/> that blocks the connection.
/// Set to zero to disable connection blocking.
/// </summary>
public int DenialsBeforeConnectionBlock { get; set; } = 5;
/// <summary>
/// Window in which denials are accumulated for connection blocking.
/// </summary>
public TimeSpan DenialWindow { get; set; } = TimeSpan.FromMinutes(1);
/// <summary>
/// Delay before a blocked connection is closed, allowing its final error reply to flush.
/// </summary>
public TimeSpan ConnectionBlockDelay { get; set; } = TimeSpan.FromMilliseconds(100);
}