Here's my current implementation of HttpService.serve()
@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
return HttpResponse.from(req.aggregate().thenApply(ahr -> {
MyResponse myResponse = Utils.handle(ahr);
HttpResponse httpResponse Utils.toResponse(myResponse);
return httpResponse;
}));
}
I have a user-defined response delay which can vary per each individual request-response, and this is available in the myResponse
object.
What is the best way to apply this delay in a non-blocking way, I can see some delay
API-s but they are protected within HttpResponse
. Any extra tips or pointers to the streaming API design or decorators would be helpful. I'm really learning a lot from the Armeria code base :)
If you know the desired delay even before consuming the request body, you can simply use HttpResponse.delayed()
:
@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
return HttpResponse.delayed(
HttpResponse.of(200),
Duration.ofSeconds(3),
ctx.eventLoop());
}
If you need to consume the content or perform some operation to calculate the desired delay, you can combine HttpResponse.delayed()
with HttpResponse.from()
:
@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
return HttpResponse.from(req.aggregate().thenApply(ahr -> {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MyResponse myResponse = Utils.handle(ahr);
HttpResponse httpResponse = Utils.toResponse(myResponse);
Duration myDelay = Utils.delayMillis(...);
return HttpResponse.delayed(httpResponse, myDelay, ctx.eventLoop());
// ^^^^^^^
});
}
If the delay is not actually delay but waiting for something to happen, you can use CompletableFuture.thenCompose()
which is more powerful:
@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
return HttpResponse.from(req.aggregate().thenCompose(ahr -> {
// ^^^^^^^^^^^
My1stResponse my1stRes = Utils.handle(ahr);
// Schedule some asynchronous task that returns another future.
CompletableFuture<My2ndResponse> myFuture = doSomething(my1stRes);
// Map the future into an HttpResponse.
return myFuture.thenApply(my2ndRes -> {
HttpResponse httpRes = Utils.toResponse(my1stRes, my2ndRes);
return httpRes;
});
});
}
For even more complicated workflow, I'd recommend you to look into Reactive Streams implementations such as Project Reactor and RxJava, which provides the tools for avoiding the callback hell. 😄