public async Task RequestRejected_WinsOverDefaultStatusCode()
    {
        var onRejectedInvoked = false;
        var options           = CreateOptionsAccessor();

        options.Value.GlobalLimiter = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));
        options.Value.OnRejected    = (context, token) =>
        {
            onRejectedInvoked = 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();
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(onRejectedInvoked);
        Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode);
    }
    public async Task GlobalAndEndpoint_BothReject_GlobalWins()
    {
        var globalOnRejectedInvoked = false;
        var options = CreateOptionsAccessor();
        var name    = "myEndpoint";

        // Endpoint never allows - it should not fire
        options.Value.AddPolicy <string>(name, new TestRateLimiterPolicy("myKey", 404, false));
        // Global never allows - it should fire
        options.Value.GlobalLimiter = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));
        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(name)), "Test endpoint"));
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(globalOnRejectedInvoked);

        Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode);
    }
    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);
    }
    public async Task RequestRejected_CallsOnRejectedAndGives503()
    {
        var onRejectedInvoked = false;
        var options           = CreateOptionsAccessor();

        options.Value.Limiter    = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));
        options.Value.OnRejected = (httpContext, lease) =>
        {
            onRejectedInvoked = true;
            return(Task.CompletedTask);
        };

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

        var context = new DefaultHttpContext();
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(onRejectedInvoked);
        Assert.Equal(StatusCodes.Status503ServiceUnavailable, context.Response.StatusCode);
    }
    public async Task EndpointLimiterRejects_EndpointOnRejectedFires()
    {
        var globalOnRejectedInvoked = false;
        var options = CreateOptionsAccessor();
        var name    = "myEndpoint";

        // This is the policy that should get used
        options.Value.AddPolicy <string>(name, new TestRateLimiterPolicy("myKey", 404, false));
        // This OnRejected should be ignored in favor of the one on the policy
        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(name)), "Test endpoint"));
        await middleware.Invoke(context).DefaultTimeout();

        Assert.False(globalOnRejectedInvoked);

        Assert.Equal(StatusCodes.Status404NotFound, context.Response.StatusCode);
    }
    public async Task EndpointLimiter_DuplicatePartitionKey_Lambda_NoCollision()
    {
        var globalOnRejectedInvoked = false;
        var options       = CreateOptionsAccessor();
        var endpointName1 = "myEndpoint1";
        var endpointName2 = "myEndpoint2";
        var duplicateKey  = "myKey";

        // Two policies with the same partition key should not collide, because DefaultKeyType has reference equality
        options.Value.AddPolicy <string>(endpointName1, key =>
        {
            return(new RateLimitPartition <string>(duplicateKey, partitionKey =>
            {
                return new TestRateLimiter(false);
            }));
        });
        options.Value.AddPolicy <string>(endpointName2, key =>
        {
            return(new RateLimitPartition <string>(duplicateKey, partitionKey =>
            {
                return new TestRateLimiter(true);
            }));
        });
        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();
        var endpoint1 = new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(new EnableRateLimitingAttribute(endpointName1)), "Test endpoint 1");
        var endpoint2 = new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(new EnableRateLimitingAttribute(endpointName2)), "Test endpoint 2");

        context.SetEndpoint(endpoint1);
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(globalOnRejectedInvoked);
        // This should hit endpointName1
        Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode);

        globalOnRejectedInvoked = false;

        context.SetEndpoint(endpoint2);
        await middleware.Invoke(context).DefaultTimeout();

        Assert.False(globalOnRejectedInvoked);
    }
    public async Task EndpointLimiterRejects_EndpointOnRejectedFires_WithIRateLimiterPolicy()
    {
        var globalOnRejectedInvoked = false;
        var options = CreateOptionsAccessor();
        var name    = "myEndpoint";

        // This is the policy that should get used
        options.Value.AddPolicy <string, TestRateLimiterPolicy>(name);
        // This OnRejected should be ignored in favor of the one on the policy
        options.Value.OnRejected = (context, token) =>
        {
            globalOnRejectedInvoked = true;
            context.HttpContext.Response.StatusCode = 429;
            return(ValueTask.CompletedTask);
        };

        // Configure the service provider with the args to the TestRateLimiterPolicy ctor
        var mockServiceProvider = new Mock <IServiceProvider>();

        mockServiceProvider
        .Setup(sp => sp.GetService(typeof(string)))
        .Returns("myKey");
        mockServiceProvider
        .Setup(sp => sp.GetService(typeof(int)))
        .Returns(404);
        mockServiceProvider
        .Setup(sp => sp.GetService(typeof(bool)))
        .Returns(false);

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

        var context = new DefaultHttpContext();

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

        Assert.False(globalOnRejectedInvoked);

        Assert.Equal(StatusCodes.Status404NotFound, context.Response.StatusCode);
    }
    public async Task EndpointLimiterRequested_NoPolicy_Throws()
    {
        var options = CreateOptionsAccessor();
        var name    = "myEndpoint";

        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(name)), "Test endpoint"));
        await Assert.ThrowsAsync <InvalidOperationException>(() => middleware.Invoke(context)).DefaultTimeout();
    }
    public async Task RequestsCallNextIfAccepted()
    {
        var flag    = false;
        var options = CreateOptionsAccessor();

        options.Value.Limiter = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(true));
        var middleware = new RateLimitingMiddleware(c =>
        {
            flag = true;
            return(Task.CompletedTask);
        },
                                                    new NullLoggerFactory().CreateLogger <RateLimitingMiddleware>(),
                                                    options);

        await middleware.Invoke(new DefaultHttpContext());

        Assert.True(flag);
    }
    public async Task RequestAborted_ThrowsTaskCanceledException()
    {
        var options = CreateOptionsAccessor();

        options.Value.Limiter = new TestPartitionedRateLimiter <HttpContext>(new TestRateLimiter(false));

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

        var context = new DefaultHttpContext();

        context.RequestAborted = new CancellationToken(true);
        await Assert.ThrowsAsync <TaskCanceledException>(() => middleware.Invoke(context)).DefaultTimeout();
    }
    public async Task EndpointLimiterConvenienceMethod_Rejects()
    {
        var onRejectedInvoked = false;
        var options           = CreateOptionsAccessor();
        var name = "myEndpoint";

        options.Value.AddFixedWindowLimiter(name, options =>
        {
            options.PermitLimit          = 1;
            options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
            options.QueueLimit           = 0;
            options.Window            = TimeSpan.Zero;
            options.AutoReplenishment = false;
        });
        options.Value.OnRejected = (context, token) =>
        {
            onRejectedInvoked = 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(name)), "Test endpoint"));
        await middleware.Invoke(context).DefaultTimeout();

        Assert.False(onRejectedInvoked);
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(onRejectedInvoked);
        Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode);
    }
    public async Task EndpointLimiter_Rejects()
    {
        var onRejectedInvoked = false;
        var options           = CreateOptionsAccessor();
        var name = "myEndpoint";

        options.Value.AddPolicy <string>(name, (context =>
        {
            return(RateLimitPartition.Get <string>("myLimiter", (key =>
            {
                return new TestRateLimiter(false);
            })));
        }));
        options.Value.OnRejected = (context, token) =>
        {
            onRejectedInvoked = 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(name)), "Test endpoint"));
        await middleware.Invoke(context).DefaultTimeout();

        Assert.True(onRejectedInvoked);
        Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode);
    }