Search code examples
amazon-web-servicesplayframeworkthreadpool

Play Framework how to purposely delay a response


We have a Play app, currently using version 2.6. We are trying to prevent dictionary attacks against our login by delaying a "failed login" message back to our users when they provide a failed password. We currently hash and salt and have all the best practices, but we are not sure if we are delaying correctly. So we have in our Controller:

public Result login() { return ok(loginHtml) }

and we have a:

public Result loginAction()
{ 
  // Check for user in database
  User user = User.find.query()...

  // Was the user found?
  if (user == null) {

     // Wrong password! Delay and redirect
     Thread.sleep(10000);  <<-- how do delay correctly?
     return redirect(routes.Controller.login())
  }

  // User is not null, so all good!
  ...

}

We are not sure if Thread.sleep(10000) is the best way to delay a response since this might hang other requests that come in, or use too many thread from the default pool. We have noticed that under 80+ hits per second the Play Framework does not route our HTTP calls to the Routes. That is, if we receive a HTTP POST request, our app will not even send that request to the Controller until 20+ seconds later, HOWEVER, in the SAME time period if we get a HTTP GET request, our app will process that GET instantly!

Currently we have 300 threads as the min/max in our Akka settings for the default fork pool. Any insights would be appreciated. We run a t2.xlarge AWS EC2 instance running Ubuntu.

Thank you.


Solution

  • Thread.sleep causes current thread blocking, please, try to avoid using it in production code as much as possible.

    What you need to use, is CompletionStage / CompletableFuture or any abstraction for deeling with async programming and asynchronous action.

    Please, take a look for more details about asynchronios actions: https://www.playframework.com/documentation/2.8.x/JavaAsync

    In your case solution would look like something too (excuse me, please, this might have mistakes - I'm Scala engineer primary):

    import play.libs.concurrent.HttpExecutionContext;
    import play.mvc.*;
    
    import javax.inject.Inject;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.CompletionStage;
    
    public class LoginController extends Controller {
    
      private HttpExecutionContext httpExecutionContext;
    
      // Create and inject separate ScheduledExecutorService
      private ScheduledExecutorService executor; 
    
      @Inject
      public LoginController(HttpExecutionContext ec,
                             ScheduledExecutorService executor) {
        this.httpExecutionContext = ec;
        this.executor = executor;
      }
    
      public CompletionStage<Result> loginAction() {
        User user = User.find.query()...
        if (user == null) {
            return executor.schedule(() -> {redirect(routes.Controller.login());}, 10, TimeUnit.SECONDS);
        } else {
          // return another response
        }
      }
    }
    

    Hope this helps!