Vert.X: Rare "Unhandled exception in router" despite errorHandler with statusCode 500 - vert.x

I use errorHandler to log interesting general information like User-ID (if available) and IP. This normally works, but rarely I see "Unhandled exception in router" that hasn't passed by my errorHandler:
ERROR io.vertx.ext.web.RoutingContext - Unhandled exception in router
My errorHandler:
router.errorHandler(500) { ctx ->
// Logging happens.
}
From documentation:
You can use to manage general errors too using status code 500. The handler will be called when the context fails and other failure handlers didn't write the reply or when an exception is thrown inside an handler.
What is missing?

An error handler is different from an exception/failure handler
router.route().failureHandler(this::handleFailure)
private fun handleFailure(routingContext: RoutingContext) {
routingContext.response()
.putHeader("Content-type", "application/json; charset=utf-8")
.setStatusCode(500)
.end(routingContext.failure().message ?: "internal server error")
}

Related

In Spring WebClient used with block(), get body on error

I am using WebClient from Spring WebFlux to communicate with a REST API backend from a Spring client.
When this REST API backend throws an exception, it answers with a specific format (ErrorDTO) that I would like to collect from my client.
What I have tried to do is to make my client throw a GestionUtilisateurErrorException(ErreurDTO) containing this body once the server answers with a 5xx HTTP status code.
I have tried several options :
I/ onStatus
#Autowired
WebClient gestionUtilisateursRestClient;
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(HttpStatus::is5xxServerError,
response -> {
ErreurDTO erreur = response.bodyToMono(ErreurDTO.class).block();
return Mono.error(new GestionUtilisateursErrorException(erreur));
}
)
.bodyToMono(Void.class)
.timeout(Duration.ofMillis(5000))
.block();
This method doesn't work because webclient doesn't allow me to call the block method in the onStatus. I am only able to get a Mono object and I can't go further from here.
It seems like "onStatus" method can't be used in a WebClient blocking method, which means I can throw a custom Exception, but I can't populate it with the data from the response body.
II/ ExchangeFilterFunction
#Bean
WebClient gestionUtilisateursRestClient() {
return WebClient.builder()
.baseUrl(gestionUtilisateursApiUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.filter(ExchangeFilterFunction.ofResponseProcessor(this::gestionUtilisateursExceptionFilter))
.build();
}
private Mono<ClientResponse> gestionUtilisateursExceptionFilter(ClientResponse clientResponse) {
if(clientResponse.statusCode().isError()){
return clientResponse.bodyToMono(ErreurDTO.class)
.flatMap(erreurDto -> Mono.error(new GestionUtilisateursErrorException(
erreurDto
)));
}
return Mono.just(clientResponse);
}
This method works but throw a reactor.core.Exceptions$ReactiveException that I am struggling to catch properly (reactor.core.Exceptions is not catchable, and ReactiveException is private).
This Exception contains in its Cause the exception I need to catch (GestionUtilisateurErrorException) but I need a way to catch it properly.
I also tried to use "onErrorMap" and "onErrorResume" methods but none of them worked the way I needed.
Edit 1 :
I am now using the following workaround even if I feel it's a dirty way to do what I need :
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
LOGGER.error(erreur.getMessage());
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)
.bodyToMono(String.class)
.timeout(Duration.ofMillis(5000))
.block();
}
catch(Exception e) {
LOGGER.debug("Erreur lors de l'appel vers l'API GestionUtilisateur (...)");
if(ExceptionUtils.getRootCause(e) instanceof GestionUtilisateursErrorException) {
throw((GestionUtilisateursErrorException) e.getCause());
}
else {
throw e;
}
}
Here, it throws the expected GestionUtilisateursErrorException that I can handle synchronously.
I might implement this in a global handler to avoid writing this code around each call to my API.
Thank you.
Kevin
I've encountered a similar case for accessing the response body that might be of use to you using the Mono.handle() method (see https://projectreactor.io/docs/core/release/api/index.html?reactor/core/publisher/Mono.html).
Here handler is a SynchronousSink (see https://projectreactor.io/docs/core/release/api/reactor/core/publisher/SynchronousSink.html) and can call at most next(T) one time, and either complete() or error().
In this case, I call 'handler.error()' with a new GestionUtilisateursErrorException constructed with the 'erreur'.
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
// Do something with erreur e.g.
log.error(erreur.getErrorMessage());
// Call handler.next() and either handler.error() or handler.complete()
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)

Abort (HTTP 500) request when exception in request's handleEnd()

I'm using vert.x-web to implement a small service. One of my handlers for the end of the request (set via context.request().endHandler()) throws this NullPointerException:
2018-09-02 20:54:35,125 - ERROR [vert.x-eventloop-thread-1] (ContextImpl.java:345) - lambda$wrapTask$2()
Unhandled exception
java.lang.NullPointerException
at (My handler class)
at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:417)
at io.vertx.core.http.impl.Http1xServerConnection.handleEnd(Http1xServerConnection.java:482)
at io.vertx.core.http.impl.Http1xServerConnection.handleContent(Http1xServerConnection.java:477)
at io.vertx.core.http.impl.Http1xServerConnection.processMessage(Http1xServerConnection.java:458)
at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:144)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandlerWithWebSockets.handleMessage(HttpServerImpl.java:712)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandlerWithWebSockets.handleMessage(HttpServerImpl.java:619)
at io.vertx.core.net.impl.VertxHandler.lambda$channelRead$1(VertxHandler.java:146)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:337)
at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:195)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:144)
Why doesn't this exception call my requests's exception handler? Why is it unhandled? I have the request's exception handler set to context.fail() (via context.request().exceptionHandler()). But it does not seem to have any effect.
Is there another exception handler I'm unaware of?
Edit: here is the minimal reproducing code:
Vertx vertx = Vertx.vertx();
Router router = Router.router(vertx);
router.route().handler(context -> {
context.request()
.exceptionHandler(context::fail)
.endHandler(nothing -> { throw new NullPointerException("null"); })
.handler(buffer -> {});
});
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(8080);
Expected behavior: context.fail(e) gets called and the connection closes with HTTP ERROR 500.
Got behavior: context is not failed, connection "hangs".
The exceptionHandler applies to the HttpServerRequest object. The method is inherited from the ReadStream interface. This callback is invoked whenever a problem occurs in the Vert.x/Netty code handling HTTP requests, not user code.
If you want to execute some code before the actual request processing, I would suggest to register a route and invoke RoutingContext#next in the handler:
Router router = Router.router(vertx);
router.route("/somepath").handler(routingContext -> {
// Handler invoked first
// Execute pre-processing logic
// And then...
context.next();
});
router.route("/somepath").handler(routingContext -> {
// Handler invoked second
// Execute processing logic
});
Then any failure in pre-processing logic will be caught and managed normally by the router.
Two things need to be pointed out here:
The hang is caused by the response is not explicitly ended (see HttpServerResponse#end().
To handle exception happened during request handling, add failure handler at route level (see Route#failureHandler()). Handling exception on request will only caught exception when reading the stream.
For example:
Router router = Router.router(vertx);
router.route().failureHandler(handler -> handler.response().end());
router.route().handler(routingContext -> routingContext.request().endHandler(handler -> {
throw new NullPointerException("exception here!");
}));
vertx.createHttpServer().requestHandler(router::accept).listen(8085);

Vertx - using the Router failureHandler for the errors in async calls

We have recently discovered the failureHandler in the Vertx router
We thought it could help us get rid of all the repetitive try-catch blocks we have. But alas, it seems that the exceptions that are thrown inside the callbacks are not caught by the failureHandler.
Example: below, the failureHandler is called only for the 3rd case:
// Get the user details
router.get("/user").handler(ctx -> {
ctx.response().headers().add("content-type", "application/json");
// some async operation
userApiImpl.getUser(ctx, httpClient, asyncResult -> {
// ctx.response().setStatusCode(404).end(); //1
// throw new RuntimeException("sth happened"); //2
ctx.fail(404); //3
});
});
// ============================
// ERROR HANDLER
// ============================
router.get("/user").failureHandler(ctx -> {
LOG.info("Error handler is in the action.");
ctx.response().setStatusCode(ctx.statusCode()).end("Error occurred in method");
});
Is this as expected?
Can we somehow declare a global try-catch in a router for the exceptions occurring in the async context?
It is expected that sending a response manually with an error code does not trigger the failure handler.
It should be triggered if:
the route path matches
a handler throws an exception or ctx.fail() is invoked
Here's an example:
Route route1 = router.get("/somepath/path1/");
route1.handler(routingContext -> {
// Let's say this throws a RuntimeException
throw new RuntimeException("something happened!");
});
Route route2 = router.get("/somepath/path2");
route2.handler(routingContext -> {
// This one deliberately fails the request passing in the status code
// E.g. 403 - Forbidden
routingContext.fail(403);
});
// Define a failure handler
// This will get called for any failures in the above handlers
Route route3 = router.get("/somepath/*");
route3.failureHandler(failureRoutingContext -> {
int statusCode = failureRoutingContext.statusCode();
// Status code will be 500 for the RuntimeException or 403 for the other failure
HttpServerResponse response = failureRoutingContext.response();
response.setStatusCode(statusCode).end("Sorry! Not today");
});
See the error handling section of the Vert.x Web documentation

Spring cloud performance tunning

I'm doing a performance test against a Spring Cloud application. When number of concurrent users exceeds 150, it starts to give "Forwarding error"
{"timestamp":1458685370986,"status":500,"error":"Internal Server Error","exception":"com.netflix.zuul.exception.ZuulException","message":"Forwarding error"}
Which parameter I should adjust to get rid of the error?
You should post your logs for the error, without that we can only guess what the exact error is. As Forwarding error reported by ZuulExcetption is a generic error.
See this link for the RibbonRoutingFilter.forward() method which actually reports this error. I'm adding the code here for the backup.
private HttpResponse forward(RestClient restClient, String service, Verb verb, String uri, Boolean retryable,
MultiValueMap<String, String> headers, MultiValueMap<String, String> params,
InputStream requestEntity) throws Exception {
Map<String, Object> info = this.helper.debug(verb.verb(), uri, headers, params,
requestEntity);
RibbonCommand command = new RibbonCommand(service, restClient, verb, uri, retryable,
headers, params, requestEntity);
try {
HttpResponse response = command.execute();
this.helper.appendDebug(info, response.getStatus(),
revertHeaders(response.getHeaders()));
return response;
}
catch (HystrixRuntimeException ex) {
info.put("status", "500");
if (ex.getFallbackException() != null
&& ex.getFallbackException().getCause() != null
&& ex.getFallbackException().getCause() instanceof ClientException) {
ClientException cause = (ClientException) ex.getFallbackException()
.getCause();
throw new ZuulException(cause, "Forwarding error", 500, cause
.getErrorType().toString());
}
throw new ZuulException(ex, "Forwarding error", 500, ex.getFailureType()
.toString());
}
}
As you can see that only viable place where the error can be generated is in command.execute(), where command is an instance of HystrixCommand. Here is a link for the execute() method in HystrixCommand.
Below is the code for backup.
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw decomposeException(e);
}
}
Here the queue() is a Future instance
Most common error that can occur with the Future is a timeout exception. Since here Future instance queue() is not bound by any timetout value, it can go on waiting for ever.
However most of the time API which make use of Future have a thread monitoring the time they take and they interrupt it after a certain period of time. Same is done by Ribbon.
If yours indeed is a timeout issue then an easy solution is to increase Ribbon timeout value by using following property.
ribbon.ReadTimeout=10000
//or
<client-name>.ribbon.ReadTimeout=10000
Time out majorly can occur if the tomcat server which hosts the service which is proxied by the Zuul has too much load. It's whole thread pool is exhausted thus resulting in the next requests having to wait for long time.
This can probably be alleviated by change the number of threads that your service tomcat has by using following property.
server.tomcat.max-threads=0
By default it's set to 0, which leaves it to the embedded server's default. In tomcat's case it's 200. See the reference maxThreads property in tomcat.
Note: To increase the thread pool size we have to make sure that the machine has that capacity to provide resources if that many threads were to be in execution simultaneously.

How to catch FacebookApiExceptions when using FacebookApp.ApiAsync in WP7?

I'm currently using Facebook C# SDK v4.2.1 and I'm trying to post something onto the user wall. It worked fine until I got an FacebookOAuthException (OAuthException) Error validating access token. error and I can't catch that exception and it crashes my app.
I'm using this call FacebookApp.ApiAsync("/me/feed", ...). Because it happens async I'm not sure where I have to put my try-catch block to catch that error but with no success
This is what I'm using:
private void shareFBButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
// ... code for preparing strings to post ...
try
{
// setup FacebookApp and params ...
app.ApiAsync("/me/feed", args, HttpMethod.Post, (o) => {
if (o.Error != null)
{
Debug.WriteLine("ERROR sharing on Facebook: " + o.Error.Message);
}
else
{
Debug.WriteLine("FB post success!");
}
}, null);
}
catch (Exception ex)
{
Debug.WriteLine("ERROR sharing on Facebook: " + ex.Message);
}
}
So can someone tell me where I have to put my try-catch block, so I can catch the OAuthException?
EDIT:
After further investigation, the FacebookOAuthExcpetion is thrown from Facebook C# SDK after the SDK catches WebException and FacebookApiException. For further information look at "Pavel Surmenok" his answer. That is exactly what is happening.
As of the moment the only solution for catching FacebookApiException (base class of all Facebook SDK exceptions) is to catch it in App.UnhandledException method. Check type of e.ExceptionObject and if it is a FacebookApiException set e.Handled to true and the app won't exit itself anymore.
I found a solution for my problem. Maybe I should rephrase my question.
"How to catch an exception which occurred on a background thread?"
Which is exactly what is happening in my original question. An exception is throw inside the Facebook C# SDK on a background thread because Api calls are executed asynchronously.
Maybe most of you already know this, but I didn't because I'm new to WP7 development.
Solution:
In App.UnhandledException event handler, just set the e.Handled flag to true. Then the app won't exit ifself.
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
// catch Facebook API exceptions
// if handled is set to true, app won't exit
if (e.ExceptionObject is FacebookApiException)
{
e.Handled = true;
// notify user of error ...
return;
}
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
Not sure if this is the right way to catch an API exception, but works fine for now.
I've reproduced this trouble. As I can see, the exception is generated in FacebookApp.ResponseCallback method. It contains "try" block with two "catch" sections (one for FacebookApiException and one for WebException). In the end of each "catch" sections the exception is being rethrown and is never been handled (that's why your app crashes). So, the debugger says you about this (rethrown) exception.
Later in "finally" section they create FacebookAsyncResult with reference to this exception in the property "Error".
I think that your solution (to handle this exception in App.UnhandledException) is the most appropriate one.
By the way, it's interesting, why SDK developers decided to rethrow exceptions in FacebookApp.ResponseCallback.
The debugger usually does a good job of indicating where the exception came from. In the debugger, you can examine the exception details and look at the nessted InnerExceptions to find the root cause.
That said, if the exception is thrown from within the app.ApiAsync call, then the catch handler that you already have would catch any exceptions. By the looks of things in the SDK (I've only looked briefly), there are certain circumstances where exceptions are caught and forwarded to the callback in the Error property, which you are also checking.
By looking at the SDK code, it would seem that the exception being thrown is actually the FacebookOAuthException; is that the case? If that is the case, then it looks like this exception is never provided to the callback, but always thrown.
If you can give more details about exactly what the exception type is and where it's thrown/caught, I might be able to give a more useful answer.
Trying to catch the exception in App.UnhandledException does not work as it is on a different thread. But you can play with the 'error reason' property from authResult before doing the query and so you will avoid to have the exception thrown.
private void FacebookLoginBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
FacebookAuthenticationResult authResult;
if (FacebookAuthenticationResult.TryParse(e.Uri, out authResult))
{
if (authResult.ErrorReason == "user_denied")
{
// do something.
}
else
{
fbApp.Session = authResult.ToSession();
loginSucceeded();
}
}