Skip to content

Commit c426c54

Browse files
committed
Trigger retry in WriteListener on SSLException, close AsyncHttpClient#1645
Motivation: There's a possibility that we have a write failure because we're trying to write on a pooled channel and the remote peer has closed the SSLEngine but we haven't process the connection close yet. In this case, we don't trigger retry, and don't forcefully close the channel. Modifications: * Trigger retry on write failure on SSLException. * Always close channel on write failure Result: Retry triggered on SSLEngine crash. No more pooled connection in stalled state.
1 parent b7026a1 commit c426c54

File tree

1 file changed

+15
-13
lines changed

1 file changed

+15
-13
lines changed

client/src/main/java/org/asynchttpclient/netty/request/WriteListener.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
import io.netty.channel.Channel;
1717
import org.asynchttpclient.handler.ProgressAsyncHandler;
1818
import org.asynchttpclient.netty.NettyResponseFuture;
19+
import org.asynchttpclient.netty.channel.ChannelState;
1920
import org.asynchttpclient.netty.channel.Channels;
2021
import org.asynchttpclient.netty.future.StackTraceInspector;
2122
import org.slf4j.Logger;
2223
import org.slf4j.LoggerFactory;
2324

25+
import javax.net.ssl.SSLException;
2426
import java.nio.channels.ClosedChannelException;
2527

2628
public abstract class WriteListener {
@@ -36,27 +38,27 @@ public abstract class WriteListener {
3638
this.notifyHeaders = notifyHeaders;
3739
}
3840

39-
private boolean abortOnThrowable(Channel channel, Throwable cause) {
40-
if (cause != null) {
41-
if (cause instanceof IllegalStateException || cause instanceof ClosedChannelException || StackTraceInspector.recoverOnReadOrWriteException(cause)) {
42-
LOGGER.debug(cause.getMessage(), cause);
43-
Channels.silentlyCloseChannel(channel);
41+
private void abortOnThrowable(Channel channel, Throwable cause) {
42+
if (future.getChannelState() == ChannelState.POOLED
43+
&& (cause instanceof IllegalStateException
44+
|| cause instanceof ClosedChannelException
45+
|| cause instanceof SSLException
46+
|| StackTraceInspector.recoverOnReadOrWriteException(cause))) {
47+
LOGGER.debug("Write exception on pooled channel, letting retry trigger", cause);
4448

45-
} else {
46-
future.abort(cause);
47-
}
48-
return true;
49+
} else {
50+
future.abort(cause);
4951
}
50-
51-
return false;
52+
Channels.silentlyCloseChannel(channel);
5253
}
5354

5455
void operationComplete(Channel channel, Throwable cause) {
5556
future.touch();
5657

57-
// The write operation failed. If the channel was cached, it means it got asynchronously closed.
58+
// The write operation failed. If the channel was pooled, it means it got asynchronously closed.
5859
// Let's retry a second time.
59-
if (abortOnThrowable(channel, cause)) {
60+
if (cause != null) {
61+
abortOnThrowable(channel, cause);
6062
return;
6163
}
6264

0 commit comments

Comments
 (0)