0
votes

I have a Vertx API where users can perform http POST, but i want to rate limit the same person/ ip address from spamming API calls, like a 30 second rate limit before they can do it again.

Where and how can i implement this rate limit in my vertx API code?

This is my starter:

public class Starter extends AbstractVerticle {
private static final Logger LOGGER = Logger.getLogger(Starter.class.getSimpleName());
private static final String HTTP_PORT = "http.port";
private static final Integer HTTP_FALLBACK_PORT = 8000;

public Starter() {
}

@Override
public void start(Promise<Void> startPromise) throws ParseException {
    vertx
        .createHttpServer()
        .requestHandler(buildRoutes(enableCors()))
        .listen(config().getInteger(HTTP_PORT, HTTP_FALLBACK_PORT),
            asyncResult -> listener(asyncResult, startPromise));


}

private Router buildRoutes(final Router router) throws ParseException {
    new MovieApi(router);
    router.errorHandler(500, this::handle500Error);
    return router;
}

private void handle500Error(RoutingContext routingContext) {
    LOGGER.log(Level.SEVERE, "Internal Server Error", routingContext.failure());
}

private Router enableCors() {
    var router = Router.router(vertx);
    router.route().handler(CorsHandler.create(".*.")
                               .allowedHeader("x-requested-with")
                               .allowedHeader("Access-Control-Allow-Origin")
                               .allowedHeader("origin")
                               .allowedHeader("Content-Type")
                               .allowedHeader("accept")
                               .allowedMethod(HttpMethod.GET)
                               .allowedMethod(HttpMethod.POST)
                               .allowedMethod(HttpMethod.DELETE)
                               .allowedMethod(HttpMethod.PUT));
    router.route().handler(BodyHandler.create());
    return router;
}

@Override
public void stop(Promise<Void> endFuture) {
    vertx.close(completionHandler -> endFuture.complete());
}

private void listener(final AsyncResult<HttpServer> res, Promise<Void> startPromise) {
    if (res.succeeded()) {
        startPromise.complete();
        LOGGER.log(Level.INFO,
            () -> String.format("Server is listening on port: %s", res.result().actualPort()));
    } else {
        startPromise.fail("Failed to start http server");
        LOGGER.log(Level.INFO,
            () -> String.format("Failed to bind on port: %s", config()
                                                                  .getInteger(HTTP_PORT, HTTP_FALLBACK_PORT)));
    }
}

public static void main(String... args) {
    var vertx = Vertx.vertx();
    vertx.deployVerticle(Starter.class.getName());
}

}

1

1 Answers

2
votes

Vert.x handlers are just an implementation of Chain of Responsibility pattern. And you can easily implement one yourself:

...
router.route().handler(BodyHandler.create());
router.route().handler(new MyRateLimiter());
...

The MyRateLimiter class will have to implement a single method:

void handle(RoutingContext rc)

How you implement the logic of rate limiting is really up to you. You can store the IPs in Redis with a TTL, or use a shared map, or some other solution that works for you.

If everything is fine, you then invoke rc.next()

If you want to ratelimit, you can use rc.fail(503), for example.