'Sending an unbidden 408 response from Netty on connection timeout
According to the specs an HTTP server should send a 408 if it hasn't received a request in a certain time. This is a bit unintuitive as it means you can send a response without having received a request. One purpose is to kill long-lived keep-alive HTTP 1.1 connections that clients haven't closed.
To do this, I added an IdleStateEvent
event and in there:
DefaultFullHttpResponse resp = new DefaultFullHttpResponse(HTTP_1_1,
HttpResponseStatus.REQUEST_TIMEOUT);
resp.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
ctx.writeAndFlush(resp)
.addListener(future -> {
System.out.println("Finished " + future.cause());
})
.addListener(ChannelFutureListener.CLOSE);
And the output:
Finished io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: cannot send more responses than requests
Is there a way to do this in Netty? Or a recommended way to close idle HTTP 1.1 connections?
Solution 1:[1]
According to the Netty javadoc you can use the IdleStateHandler
class to close idle connections. Apparently, this handler will trigger an IdleStateEvent
when a connection has no reads, or no writes, or both, for a period of time. This event can then be used to trigger shutting down a connection ... or to do other things.
The following example is copied from the javadoc:
// An example that sends a ping message when there is no outbound traffic
// for 30 seconds. The connection is closed when there is no inbound traffic
// for 60 seconds.
public class MyChannelInitializer extends ChannelInitializer<Channel> {
@Override
public void initChannel(Channel channel) {
channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(60, 30, 0));
channel.pipeline().addLast("myHandler", new MyHandler());
}
}
// Handler should handle the IdleStateEvent triggered by IdleStateHandler.
public class MyHandler extends ChannelDuplexHandler {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
ctx.close();
} else if (e.state() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush(new PingMessage());
}
}
}
}
ServerBootstrap bootstrap = ...;
...
bootstrap.childHandler(new MyChannelInitializer());
...
Note: according to this Q&A, the IdleStateHandler
should be the first handler in the pipeline.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Stephen C |