exceptionCaught error handling write back response

2019-07-29 03:20发布

问题:

I'm trying to genericize my error handling for HTTP requests, and always respond with an actual error HTTP code and relevant message.

Here is my issue: in my Handler which extends SimpleChannelUpstreamHandler, exceptionCaught is fired when an exception is thrown anywhere in the stack. That's good. The bad part is that when I try to write out a response to the client with an appropriate HTTP code and response, the write causes an exception, and then the program goes into an infinite loop with calling exceptionCaught over and over again.

This has to be a commonly done task. How can I do it correctly?

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent event)
        throws Exception {

    try{
        logger.error("Error in handling call: ", event.getCause());
        HttpResponse httpResponse = buildHttpResponseObject(UNAUTHORIZED, new StringBuilder("Test"), false, null);

        // Write the response.
        // for some reason calling this next line causes an infinite loop of exceptionCaught, even with the
        // catch below.  No idea why.  Still investigating.  In the meantime, we don't have custom error code responses. 
        ChannelFuture future = event.getChannel().write(httpResponse);

        // Close the connection after the write operation is done, even if it's a keep-alive.
        future.addListener(ChannelFutureListener.CLOSE);
    }
    catch(Exception t){            
        logger.error("Unable to customize error response. ", t);
        event.getChannel().close();

    }
}

Here are the exceptions:

38161 [New I/O server worker #1-1] ERROR nettytests.http.snoop.HttpSnoopServerHandler - Error in handling call: 
javax.net.ssl.SSLHandshakeException: null cert chain
    at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1364)
    at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:513)
    at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:790)
    at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
    at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
    at org.jboss.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:931)
    at org.jboss.netty.handler.ssl.SslHandler.decode(SslHandler.java:649)
    at org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:288)
    at org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:207)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
    at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
Caused by: javax.net.ssl.SSLHandshakeException: null cert chain
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1639)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:278)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:266)
    at sun.security.ssl.ServerHandshaker.clientCertificate(ServerHandshaker.java:1627)
    at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:176)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:808)
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:806)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1301)
    at org.jboss.netty.handler.ssl.SslHandler$3.run(SslHandler.java:1060)
    at org.jboss.netty.handler.ssl.ImmediateExecutor.execute(ImmediateExecutor.java:31)
    at org.jboss.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1057)
    at org.jboss.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:947)
    ... 11 more
38177 [New I/O server worker #1-1] ERROR nettytests.http.snoop.HttpSnoopServerHandler - Error in handling call: 
java.lang.IllegalStateException: cannot send more responses than requests
    at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
    at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
38177 [New I/O server worker #1-1] ERROR nettytests.http.snoop.HttpSnoopServerHandler - Error in handling call: 
java.lang.IllegalStateException: cannot send more responses than requests
    at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
    at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
38224 [New I/O server worker #1-1] ERROR nettytests.http.snoop.HttpSnoopServerHandler - Error in handling call: 
java.lang.IllegalStateException: cannot send more responses than requests
    at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
    at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
38255 [New I/O server worker #1-1] ERROR nettytests.http.snoop.HttpSnoopServerHandler - Error in handling call: 
java.lang.IllegalStateException: cannot send more responses than requests
    at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.write(Channels.java:605)
    at org.jboss.netty.channel.Channels.write(Channels.java:572)
    at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
    at nettytests.http.snoop.HttpSnoopServerHandler.exceptionCaught(HttpSnoopServerHandler.java:201)
    at nettytests.logger.RequestAuditLogger.handleUpstream(RequestAuditLogger.java:32)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.exceptionCaught(ReplayingDecoder.java:456)
    at org.jboss.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:554)
    at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:426)
    at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:47)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:343)
    at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:274)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:194)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)

... repeat ad infinitum.

回答1:

Infinity loop is bad.. what I noticed is that you call Channel.close() in the finally block. This will most times happen before you can write something to the Channel. You should use the

future.addListener(ChannelFutureListener.CLOSE);

To Close the channel after you have written something. This should even get called on an Exception