Skip to content

Commit

Permalink
Merge pull request #3 from jezzsantos/master
Browse files Browse the repository at this point in the history
Declarative Limits
  • Loading branch information
wwwlicious committed Aug 13, 2018
2 parents 06f77ba + 7a1887b commit cd12bed
Show file tree
Hide file tree
Showing 26 changed files with 747 additions and 194 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,7 @@ FakesAssemblies/
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml

#Testing Tools
*.ncrunchsolution
*.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
namespace ServiceStack.RateLimit.Redis
{
using System.Collections.Generic;
using System.Linq;
using Configuration;
using Interfaces;
Expand All @@ -12,34 +11,19 @@ namespace ServiceStack.RateLimit.Redis
using Utilities;
using Web;

public class LimitProviderBase : ILimitProvider
public class AppSettingsLimitProvider : ILimitProvider
{
private const string ScriptKey = "script:ratelimit";
private const int DefaultPerMinute = 10;
private const int DefaultPerHour = 30;

private readonly ILimitKeyGenerator keyGenerator;
private readonly LimitGroup defaultLimits;
private readonly IAppSettings appSettings;
private readonly ILog log = LogManager.GetLogger(typeof(LimitProviderBase));
private readonly ILog log = LogManager.GetLogger(typeof(AppSettingsLimitProvider));

public LimitProviderBase(ILimitKeyGenerator keyGenerator, IAppSettings appSettings)
public AppSettingsLimitProvider(ILimitKeyGenerator keyGenerator, IAppSettings appSettings)
{
keyGenerator.ThrowIfNull(nameof(keyGenerator));
appSettings.ThrowIfNull(nameof(appSettings));

this.keyGenerator = keyGenerator;
this.appSettings = appSettings;

// This is purely to ensure that we always have a default limit
defaultLimits = new LimitGroup
{
Limits = new List<LimitPerSecond>
{
new LimitPerSecond { Seconds = 60, Limit = DefaultPerMinute },
new LimitPerSecond { Seconds = 3600, Limit = DefaultPerHour }
}
};
}

public Limits GetLimits(IRequest request)
Expand All @@ -50,14 +34,14 @@ public Limits GetLimits(IRequest request)
return new Limits
{
// Return default if none found
Request = requestLimits.HasValue ? requestLimits.Value : defaultLimits,
Request = requestLimits.HasValue ? requestLimits.Value : LimitProviderConstants.DefaultLimits,
User = userLimits.HasValue ? userLimits.Value : null
};
}

public string GetRateLimitScriptId()
{
return appSettings.GetString(ScriptKey);
return appSettings.GetString(LimitProviderConstants.ScriptKey);
}

protected virtual Maybe<LimitGroup> GetConfigLimit(params string[] keys)
Expand Down
43 changes: 43 additions & 0 deletions src/ServiceStack.RateLimit.Redis/AttributeLimitProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// // This Source Code Form is subject to the terms of the Mozilla Public
// // License, v. 2.0. If a copy of the MPL was not distributed with this
// // file, You can obtain one at http://mozilla.org/MPL/2.0/.

namespace ServiceStack.RateLimit.Redis
{
using Configuration;
using Interfaces;
using Models;
using Web;

public class AttributeLimitProvider : ILimitProvider
{
private readonly IAppSettings appSettings;

public AttributeLimitProvider(IAppSettings appSettings)
{
appSettings.ThrowIfNull(nameof(appSettings));

this.appSettings = appSettings;
}

public Limits GetLimits(IRequest request)
{
var limits = request.Items.GetValueOrDefault(LimitRateAttribute.RequestItemName) as Limits;
if (limits != null)
{
return limits;
}

return new Limits
{
Request = LimitProviderConstants.DefaultLimits,
User = null
};
}

public string GetRateLimitScriptId()
{
return appSettings.GetString(LimitProviderConstants.ScriptKey);
}
}
}
2 changes: 1 addition & 1 deletion src/ServiceStack.RateLimit.Redis/LimitKeyGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public virtual string GetRequestId(IRequest request)

public virtual string GetConsumerId(IRequest request)
{
if (AuthenticateService.AuthProviders == null)
if (AuthenticateService.GetAuthProviders() == null)
{
throw new InvalidOperationException(
"AuthService not initialized. This is required for generating default ConsumerId for RateLimitting.");
Expand Down
24 changes: 24 additions & 0 deletions src/ServiceStack.RateLimit.Redis/LimitProviderConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
namespace ServiceStack.RateLimit.Redis
{
using System.Collections.Generic;
using Models;

public class LimitProviderConstants
{
public const string ScriptKey = "script:ratelimit";
public const int DefaultPerMinute = 10;
public const int DefaultPerHour = 30;

public static readonly LimitGroup DefaultLimits=new LimitGroup
{
Limits = new List<LimitPerSecond>
{
new LimitPerSecond { Seconds = 60, Limit = DefaultPerMinute },
new LimitPerSecond { Seconds = 3600, Limit = DefaultPerHour }
}
};
}
}
114 changes: 114 additions & 0 deletions src/ServiceStack.RateLimit.Redis/LimitRateAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// // This Source Code Form is subject to the terms of the Mozilla Public
// // License, v. 2.0. If a copy of the MPL was not distributed with this
// // file, You can obtain one at http://mozilla.org/MPL/2.0/.

namespace ServiceStack.RateLimit.Redis
{
using System;
using System.Collections.Generic;
using System.Linq;
using Models;
using Web;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class LimitRateAttribute : RequestFilterAttribute
{
public const string RequestItemName = "RateLimit.Limits";

public LimitRateAttribute() : this(LimitType.PerRequest, LimitProviderConstants.DefaultPerMinute,
(int) RatePeriod.PerMinute)
{
}

public LimitRateAttribute(LimitType type) : this(type, LimitProviderConstants.DefaultPerMinute,
(int) RatePeriod.PerMinute)
{
}

public LimitRateAttribute(int limit, int seconds) : this(LimitType.PerRequest, limit, seconds)
{
}

public LimitRateAttribute(int limit, RatePeriod period = RatePeriod.PerMinute) : this(LimitType.PerRequest,
limit, (int) period)
{
}

public LimitRateAttribute(LimitType type, int limit, RatePeriod period = RatePeriod.PerMinute) : this(type,
limit, (int) period)
{
}

public LimitRateAttribute(LimitType type, int limit, int seconds)
{
if (limit <= 0)
{
throw new ArgumentOutOfRangeException(nameof(limit));
}
if (seconds <= 0)
{
throw new ArgumentOutOfRangeException(nameof(seconds));
}

Limit = limit;
Seconds = seconds;
Type = type;
}

public int Limit { get; }

public int Seconds { get; }

public LimitType Type { get; }

public override void Execute(IRequest req, IResponse res, object requestDto)
{
var limits = req.Items.GetValueOrDefault(RequestItemName) as Limits;
if (limits == null)
{
req.Items[RequestItemName] = new Limits
{
Request = Type == LimitType.PerRequest
? new LimitGroup
{
Limits = new List<LimitPerSecond>
{
this.ConvertTo<LimitPerSecond>()
}
}
: null,
User = Type == LimitType.PerUser
? new LimitGroup
{
Limits = new List<LimitPerSecond>
{
this.ConvertTo<LimitPerSecond>()
}
}
: null
};
}
else
{
var group = Type == LimitType.PerRequest ? limits.Request : limits.User;
var limit = (group ?? new LimitGroup()).Limits.Safe().ToList();
limit.Add(this.ConvertTo<LimitPerSecond>());
group.Limits = limit;
}
}
}

public enum RatePeriod
{
PerSecond = 1,
PerMinute = 60,
PerHour = 3600,
PerDay = 86400
}

public enum LimitType
{
PerUser = 0,
PerRequest = 1
}
}
2 changes: 1 addition & 1 deletion src/ServiceStack.RateLimit.Redis/RateLimitFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private void EnsureDependencies(IAppHost appHost)
KeyGenerator = new LimitKeyGenerator();

if (LimitProvider == null)
LimitProvider = new LimitProviderBase(KeyGenerator, appHost.AppSettings);
LimitProvider = new AppSettingsLimitProvider(KeyGenerator, appHost.AppSettings);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,41 +30,44 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ServiceStack, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.4.0.56\lib\net40\ServiceStack.dll</HintPath>
<Reference Include="ServiceStack, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.4.5.14\lib\net45\ServiceStack.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Client, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Client.4.0.56\lib\net40\ServiceStack.Client.dll</HintPath>
<Reference Include="ServiceStack.Client, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Client.4.5.14\lib\net45\ServiceStack.Client.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Common, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Common.4.0.56\lib\net40\ServiceStack.Common.dll</HintPath>
<Reference Include="ServiceStack.Common, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Common.4.5.14\lib\net45\ServiceStack.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Interfaces.4.0.56\lib\portable-wp80+sl5+net40+win8+wpa81+monotouch+monoandroid+xamarin.ios10\ServiceStack.Interfaces.dll</HintPath>
<HintPath>..\packages\ServiceStack.Interfaces.4.5.14\lib\portable-wp80+sl5+net45+win8+wpa81+monotouch+monoandroid+xamarin.ios10\ServiceStack.Interfaces.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Redis, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Redis.4.0.56\lib\net40\ServiceStack.Redis.dll</HintPath>
<Reference Include="ServiceStack.Redis, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Redis.4.5.14\lib\net45\ServiceStack.Redis.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Text.4.0.56\lib\net40\ServiceStack.Text.dll</HintPath>
<Reference Include="ServiceStack.Text, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ServiceStack.Text.4.5.14\lib\net45\ServiceStack.Text.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="AttributeLimitProvider.cs" />
<Compile Include="Headers\RateLimitStatus.cs" />
<Compile Include="Headers\RateLimitHeader.cs" />
<Compile Include="Headers\HttpHeaders.cs" />
<Compile Include="Interfaces\ILimitKeyGenerator.cs" />
<Compile Include="Interfaces\ILimitProvider.cs" />
<Compile Include="LimitKeyGenerator.cs" />
<Compile Include="LimitProviderBase.cs" />
<Compile Include="AppSettingsLimitProvider.cs" />
<Compile Include="LimitProviderConstants.cs" />
<Compile Include="LimitRateAttribute.cs" />
<Compile Include="Models\LimitPerSecond.cs" />
<Compile Include="Models\LimitGroup.cs" />
<Compile Include="Models\Limits.cs" />
Expand Down
12 changes: 6 additions & 6 deletions src/ServiceStack.RateLimit.Redis/packages.config
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ServiceStack" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack.Client" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack.Common" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack.Interfaces" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack.Redis" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack.Text" version="4.0.56" targetFramework="net452" />
<package id="ServiceStack" version="4.5.14" targetFramework="net452" />
<package id="ServiceStack.Client" version="4.5.14" targetFramework="net452" />
<package id="ServiceStack.Common" version="4.5.14" targetFramework="net452" />
<package id="ServiceStack.Interfaces" version="4.5.14" targetFramework="net452" />
<package id="ServiceStack.Redis" version="4.5.14" targetFramework="net452" />
<package id="ServiceStack.Text" version="4.5.14" targetFramework="net452" />
</packages>
22 changes: 11 additions & 11 deletions test/DemoService/DemoService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,28 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ServiceStack, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.4.0.56\lib\net40\ServiceStack.dll</HintPath>
<Reference Include="ServiceStack, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.4.5.14\lib\net45\ServiceStack.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Client, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Client.4.0.56\lib\net40\ServiceStack.Client.dll</HintPath>
<Reference Include="ServiceStack.Client, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Client.4.5.14\lib\net45\ServiceStack.Client.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Common, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Common.4.0.56\lib\net40\ServiceStack.Common.dll</HintPath>
<Reference Include="ServiceStack.Common, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Common.4.5.14\lib\net45\ServiceStack.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Interfaces.4.0.56\lib\portable-wp80+sl5+net40+win8+wpa81+monotouch+monoandroid+xamarin.ios10\ServiceStack.Interfaces.dll</HintPath>
<HintPath>..\..\src\packages\ServiceStack.Interfaces.4.5.14\lib\portable-wp80+sl5+net45+win8+wpa81+monotouch+monoandroid+xamarin.ios10\ServiceStack.Interfaces.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Redis, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Redis.4.0.56\lib\net40\ServiceStack.Redis.dll</HintPath>
<Reference Include="ServiceStack.Redis, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Redis.4.5.14\lib\net45\ServiceStack.Redis.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.0.56.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Text.4.0.56\lib\net40\ServiceStack.Text.dll</HintPath>
<Reference Include="ServiceStack.Text, Version=4.5.14.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\src\packages\ServiceStack.Text.4.5.14\lib\net45\ServiceStack.Text.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
Expand Down
Loading

0 comments on commit cd12bed

Please sign in to comment.