vert.x
vert.x copied to clipboard
HttpServerRequest connection.close() does not trigger exceptionHandler()
Say we do something like this on HTTP server side with new HTTP request:
HttpServer server = vertx.createHttpServer(new HttpServerOptions().setTcpKeepAlive(true));
server.requestHandler(request -> {
var n = 100;
var chunk = new byte[512 * 1024];
request.response()
.putHeader("content-length", Integer.toString(n * chunk.length))
.putHeader("content-type", "text/plain");
for (int i = 0; i < n; i++) {
request.response().write(Buffer.buffer(chunk));
}
// install exceptionHandler with expectation this will callback once server or client close connection
request.response().exceptionHandler(e -> {
LOGGER.info("exceptionHandler()");
});
// close the connection after 2s (as demonstration)
vertx.setTimer(2000, unused -> {
LOGGER.info("connection().close()");
request.connection().close();
});
});
On client side, we read few bytes and then go idle, not consuming body but holding on connection. The client not accepting any data is critical piece here.
In Vert.x 3.x, the exceptionHandler() was triggered directly after server forcibly abort the request with request.connection().close().
However in Vert.x 4.x, we can see exceptionHandler() is triggered only after the client disconnects.
I found out that exceptionHandler() is not being called in Vert.x only if n is larger, so that some of write()s are pending in internal Vert.x queue – pending writes seems to block the effect of request.connection().close().
Is this expected? Is there other way to force close connection on server side? Thanks.
I found out it that ((Http1xServerConnection) request.connection()).channelHandlerContext().close(); seems to do what request.connection().close() used to do in Vert.x 3
@marekscholle if the response has been sent to the client (resp.end()) before the underlying HTTP/1 connection is closed then indeed the exception handler should not be called.
At the HTTP response level you can use HttpServerResponse#reset to close the HTTP/1 connection if the response has not been fully sent.
The issue does not seem to report a precise bug or misbehavior, I tried the following reproducers:
@Test
public void testSome() throws Exception {
server.requestHandler(req -> {
HttpServerResponse resp = req.response();
resp.setChunked(true);
resp.exceptionHandler(err -> {
System.out.println("ERR");
err.printStackTrace(System.out);
});
resp.closeHandler(v -> {
System.out.println("closed");
});
vertx.runOnContext(v -> {
resp.reset();
});
});
startServer(testAddress);
client.request(requestOptions).compose(req -> req.send()).await();
await();
}
and
private void sendUntilFUll(HttpServerResponse resp, Handler<Void> h) {
if (resp.writeQueueFull()) {
h.handle(null);
} else {
resp.write(TestUtils.randomAlphaString(1024 * 1024));
vertx.runOnContext(v -> {
sendUntilFUll(resp, h);
});
}
}
@Test
public void testSome() throws Exception {
server.requestHandler(req -> {
HttpServerResponse resp = req.response();
resp.setChunked(true);
resp.exceptionHandler(err -> {
System.out.println("ERR");
err.printStackTrace(System.out);
});
resp.closeHandler(v -> {
System.out.println("closed");
});
sendUntilFUll(resp, v -> {
System.out.println("FULL");
resp.reset();
});
});
startServer(testAddress);
client.request(requestOptions).compose(req -> req.send()).await();
await();
}
both triggered the exception handler and the close handler on the response.