public async Task PolicyDirectlyOnEndpoint_GetsUsed()
    {
        var globalOnRejectedInvoked = false;
        var options = CreateOptionsAccessor();
        // Policy will disallow
        var policy = new TestRateLimiterPolicy("myKey", 404, false);
        var defaultRateLimiterPolicy = new DefaultRateLimiterPolicy(RateLimiterOptions.ConvertPartitioner <string>(null, policy.GetPartition), policy.OnRejected);

        options.Value.OnRejected = (context, token) =>
        {
            globalOnRejectedInvoked = true;
            context.HttpContext.Response.StatusCode = 429;
            return(ValueTask.CompletedTask);
        };

        var middleware = new RateLimitingMiddleware(c =>
        {
            return(Task.CompletedTask);
        },
                                                    new NullLoggerFactory().CreateLogger <RateLimitingMiddleware>(),
                                                    options,
                                                    Mock.Of <IServiceProvider>());

        var context = new DefaultHttpContext();

        context.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(new EnableRateLimitingAttribute(defaultRateLimiterPolicy)), "Test endpoint"));
        await middleware.Invoke(context).DefaultTimeout();

        Assert.False(globalOnRejectedInvoked);

        Assert.Equal(StatusCodes.Status404NotFound, context.Response.StatusCode);
    }
Example #2
0
    public void AddPolicy_Generic_ThrowsOnDuplicateName()
    {
        var options = new RateLimiterOptions();

        options.AddPolicy <string>("myKey", context => RateLimitPartition.GetNoLimiter <string>("myKey"));
        Assert.Throws <ArgumentException>(() => options.AddPolicy <string, TestRateLimiterPolicy>("myKey"));
    }
    /// <summary>
    /// Enables rate limiting for the application.
    /// </summary>
    /// <param name="app"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static IApplicationBuilder UseRateLimiter(this IApplicationBuilder app, RateLimiterOptions options)
    {
        ArgumentNullException.ThrowIfNull(app);
        ArgumentNullException.ThrowIfNull(options);

        return(app.UseMiddleware <RateLimitingMiddleware>(Options.Create(options)));
    }
Example #4
0
    public void UseRateLimiter_RespectsOptions()
    {
        // These are the options that should get used
        var options = new RateLimiterOptions();

        options.RejectionStatusCode = 429;
        options.GlobalLimiter       = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));

        // These should not get used
        var services = new ServiceCollection();

        services.Configure <RateLimiterOptions>(options =>
        {
            options.GlobalLimiter       = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));
            options.RejectionStatusCode = 404;
        });
        services.AddLogging();
        var serviceProvider = services.BuildServiceProvider();
        var appBuilder      = new ApplicationBuilder(serviceProvider);

        // Act
        appBuilder.UseRateLimiter(options);
        var app     = appBuilder.Build();
        var context = new DefaultHttpContext();

        app.Invoke(context);
        Assert.Equal(429, context.Response.StatusCode);
    }
    /// <summary>
    /// Adds a new <see cref="ConcurrencyLimiter"/> with the given <see cref="ConcurrencyLimiterOptions"/> to the <see cref="RateLimiterOptions"/>.
    /// </summary>
    /// <param name="options">The <see cref="RateLimiterOptions"/> to add a limiter to.</param>
    /// <param name="policyName">The name that will be associated with the limiter.</param>
    /// <param name="configureOptions">A callback to configure the <see cref="ConcurrencyLimiterOptions"/> to be used for the limiter.</param>
    /// <returns>This <see cref="RateLimiterOptions"/>.</returns>
    public static RateLimiterOptions AddConcurrencyLimiter(this RateLimiterOptions options, string policyName, Action <ConcurrencyLimiterOptions> configureOptions)
    {
        ArgumentNullException.ThrowIfNull(configureOptions);

        var key = new PolicyNameKey()
        {
            PolicyName = policyName
        };
        var concurrencyLimiterOptions = new ConcurrencyLimiterOptions();

        configureOptions.Invoke(concurrencyLimiterOptions);
        return(options.AddPolicy(policyName, context =>
        {
            return RateLimitPartition.GetConcurrencyLimiter(key,
                                                            _ => concurrencyLimiterOptions);
        }));
    }
    /// <summary>
    /// Adds a new <see cref="SlidingWindowRateLimiter"/> with the given <see cref="SlidingWindowRateLimiterOptions"/> to the <see cref="RateLimiterOptions"/>.
    /// </summary>
    /// <param name="options">The <see cref="RateLimiterOptions"/> to add a limiter to.</param>
    /// <param name="policyName">The name that will be associated with the limiter.</param>
    /// <param name="configureOptions">A callback to configure the <see cref="SlidingWindowRateLimiterOptions"/> to be used for the limiter.</param>
    /// <returns>This <see cref="RateLimiterOptions"/>.</returns>
    public static RateLimiterOptions AddSlidingWindowLimiter(this RateLimiterOptions options, string policyName, Action <SlidingWindowRateLimiterOptions> configureOptions)
    {
        ArgumentNullException.ThrowIfNull(configureOptions);

        var key = new PolicyNameKey()
        {
            PolicyName = policyName
        };
        var slidingWindowRateLimiterOptions = new SlidingWindowRateLimiterOptions();

        configureOptions.Invoke(slidingWindowRateLimiterOptions);
        // Saves an allocation in GetSlidingWindowLimiter, which would have created a new set of options if this was true.
        slidingWindowRateLimiterOptions.AutoReplenishment = false;
        return(options.AddPolicy(policyName, context =>
        {
            return RateLimitPartition.GetSlidingWindowLimiter(key,
                                                              _ => slidingWindowRateLimiterOptions);
        }));
    }
    public void ThrowsOnNullOnRejected()
    {
        var options = new RateLimiterOptions();

        Assert.Throws <ArgumentNullException>(() => options.OnRejected = null);
    }
    public void ThrowsOnNullLimiter()
    {
        var options = new RateLimiterOptions();

        Assert.Throws <ArgumentNullException>(() => options.Limiter = null);
    }
Example #9
0
    /// <summary>
    /// Adds the specified rate limiting policy to the endpoint(s).
    /// </summary>
    /// <param name="builder">The endpoint convention builder.</param>
    /// <param name="policy">The rate limiting policy to add to the endpoint.</param>
    /// <returns>The original convention builder parameter.</returns>
    public static TBuilder RequireRateLimiting <TBuilder, TPartitionKey>(this TBuilder builder, IRateLimiterPolicy <TPartitionKey> policy) where TBuilder : IEndpointConventionBuilder
    {
        ArgumentNullException.ThrowIfNull(builder);

        ArgumentNullException.ThrowIfNull(policy);

        builder.Add(endpointBuilder =>
        {
            endpointBuilder.Metadata.Add(new EnableRateLimitingAttribute(new DefaultRateLimiterPolicy(RateLimiterOptions.ConvertPartitioner <TPartitionKey>(null, policy.GetPartition), policy.OnRejected)));
        });
        return(builder);
    }
Example #10
0
    public void AddPolicy_ThrowsOnNullPolicy()
    {
        var options = new RateLimiterOptions();

        Assert.Throws <ArgumentNullException>(() => options.AddPolicy <string>("myKey", policy: null));
    }
Example #11
0
    public void AddPolicy_ThrowsOnNullPolicyName()
    {
        var options = new RateLimiterOptions();

        Assert.Throws <ArgumentNullException>(() => options.AddPolicy <string>(null, context => RateLimitPartition.GetNoLimiter <string>("myKey")));
    }