Abort (HTTP 500) request when exception in request's handleEnd() - vert.x

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

Related

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

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")
}

SignalR Core - Error: Websocket closed with status code: 1006

I use SignalR in an Angular app. When I destroy component in Angular I also want to stop connection to the hub. I use the command:
this.hubConnection.stop();
But I get an error in Chrome console:
Websocket closed with status code: 1006
In Edge: ERROR Error: Uncaught (in promise): Error: Invocation canceled due to connection being closed. Error: Invocation canceled due to connection being closed.
It actually works and connection has been stopped, but I would like to know why I get the error.
This is how I start the hub:
this.hubConnection = new HubConnectionBuilder()
.withUrl("/matchHub")
.build();
this.hubConnection.on("MatchUpdate", (match: Match) => {
// some magic
})
this.hubConnection
.start()
.then(() => {
this.hubConnection.invoke("SendUpdates");
});
EDIT
I finally find the issue. Its caused by change streams from Mongo. If I remove the code from SendUpdates() method then OnDisconnected is triggered.
public class MatchHub : Hub
{
private readonly IMatchManager matchManager;
public MatchHub(IMatchManager matchManager)
{
this.matchManager = matchManager;
}
public async Task SendUpdates() {
using (var changeStream = matchManager.GetChangeStream()) {
while (changeStream.MoveNext()) {
var changeStreamDocument = changeStream.Current.FullDocument;
if (changeStreamDocument == null) {
changeStreamDocument = BsonSerializer.Deserialize<Match>(changeStream.Current.DocumentKey);
}
await Clients.Caller.SendAsync("MatchUpdate", changeStreamDocument);
}
}
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
}
}
Method GetChangeStream from the manager.
ChangeStreamOptions options = new ChangeStreamOptions() { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var watch = mongoDb.Matches.Watch(options).ToEnumerable().GetEnumerator();
return watch;
But I don't know how to fix it.
This can be for many reasons but i think it is most likely this one:
I think this is because of how the server is handling the connected / disconnected events. I can't say for sure but the connection closing needs to handled correctly on the server also with code. Try overriding the built in On Connected /Disconnected methods on the server and see. My assumption only is that you're closing it but the server isn't closing properly and therefore not relaying the proper closed response.
found as a comment at : getting the reason why websockets closed with close code 1006
Where you don't need to change the connection/disconection because evrything works fine. But as an answer this one is the most likely.
It throws error because the callback doesn't get clear properly.
And it is caused by the return data from websocket.
normally it should return like
However, for some reason it might return something like
the very last response breaking into 2 pieces
And that causes the issue.
I don't think there is a way to bypass this without changing the source code.
I reported this on github repo as well at here
It turns out that I can just utilize invocation response to notify client to stop the hub. So it doesn't trigger racing issue.

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.

Nodejs: How to catch exception in net.createServer.on("data",...)?

I've got a standard socket-server (NO HTTP) setup as follows (contrived):
var server = net.createServer(function(c) { //'connection' listener
c.on('data', function(data) {
//do stuff here
//some stuff can result in an exception that isn't caught anywhere downstream,
//so it bubbles up. I try to catch it here.
//this is the same problem as just trying to catch this:
throw new Error("catch me if you can");
});
}).listen(8124, function() { //'listening' listener
console.log('socket server started on port 8124,');
});
Now the thing is I've got some code throwing errors that aren't catched at all, crashing the server. As a last measure I'd like to catch them on this level, but anything I've tried fails.
server.on("error",....)
c.on("error",...)
Perhaps I need to get to the socket instead of c (the connection), although I'm not sure how.
I'm on Node 0.6.9
Thanks.
process.on('uncaughtException',function(err){
console.log('something terrible happened..')
})
You should catch the Exceptions yourself. There is no event on either connection or server objects which would allow you to handle exception the way you described. You should add exception handling logic into your event handlers to avoid server crash like this:
c.on('data', function(data) {
try {
// even handling code
}
catch(exception) {
// exception handling code
}