0

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());
}

}

ZedPython
  • 121
  • 2

1 Answers1

2

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.

Alexey Soshin
  • 16,718
  • 2
  • 31
  • 40