stefanprodan / WebApiThrottle

ASP.NET Web API rate limiter for IIS and Owin hosting
MIT License
1.28k stars 274 forks source link

ArgumentNullException when using ClientRules #74

Closed nickbeaugie closed 8 years ago

nickbeaugie commented 8 years ago

I am updating an existing application that has used WebApiThrottle for a number of years.

I want to make use of the ClientRules feature. This should be as simple as adding a few extra lines to the creation of the policy as shown here.

        var policy = new ThrottlePolicy(
            DefaultRateLimits.PerSecond == 0 ? null : (int?) DefaultRateLimits.PerSecond,
            DefaultRateLimits.PerMinute == 0 ? null : (int?) DefaultRateLimits.PerMinute,
            DefaultRateLimits.PerHour == 0 ? null : (int?) DefaultRateLimits.PerHour,
            DefaultRateLimits.PerDay == 0 ? null : (int?) DefaultRateLimits.PerDay,
            DefaultRateLimits.PerWeek == 0 ? null : (int?) DefaultRateLimits.PerWeek);
        policy.IpThrottling = IpThrottling;
        policy.EndpointThrottling = EndpointThrottling;
        policy.ClientThrottling = ClientThrottling;
        policy.ClientWhitelist = WhitelistedAppIds;

        policy.ClientRules = new Dictionary<string, RateLimits>();
        policy.ClientRules.Add("dummyApp", new RateLimits
        {
            PerSecond = 3
        });

However, when running the application, I get the following exception.

An error has occurred.Value cannot be null. Parameter name: keySystem.ArgumentNullException at System.Collections.Generic.Dictionary`2.FindEntry(TKey key) at System.Collections.Generic.Dictionary`2.KeyCollection.System.Collections.Generic.ICollection<TKey>.Contains(TKey item) at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value) at WebApiThrottle.ThrottlingCore.ApplyRules(RequestIdentity identity, TimeSpan timeSpan, RateLimitPeriod rateLimitPeriod, Int64& rateLimit) at WebApiThrottle.ThrottlingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Is there something I've missed? I've tried all kinds of variations, too tedious to list here.

stefanprodan commented 8 years ago

I suspect the Identity.ClientKey is null. Have you implemented a client key retrieval ?

nickbeaugie commented 8 years ago

@stefanprodan - that is indeed the problem!

This was my existing code:

    protected override RequestIdentity SetIndentity(HttpRequestMessage request)
    {
        var authContext = AuthContext.GetCurrent();
        var clientIp = request.GetClientOrForwardedForIpAddress();
        return new RequestIdentity
        {
            ClientKey = authContext != null ? authContext.ClientId : null,
            ClientIp = clientIp != null ? clientIp.ToString() : null,
            Endpoint = request.RequestUri.AbsolutePath
        };
    }

I changed this one line:

            ClientKey = authContext != null ? authContext.ClientId : "anon",

And that solved it. Thanks so much!

The thing is, this is all mature code that my predecessor wrote and has worked well in production. It must be that ClientRules are handled in a different way.