Skip to content

Commit be6a90c

Browse files
NettyUtils: added safe check for null pointers. Added regression tests. (#3938)
1 parent 6530cd7 commit be6a90c

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "Netty NIO HTTP Client",
3+
"contributor": "martinKindall",
4+
"type": "bugfix",
5+
"description": "The fix involves implementing a null-pointer check in the NettyUtils#isConnectionResetException() method, in case the\n throwable of the original cause has no message."
6+
}

http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ public static Throwable decorateException(Channel channel, Throwable originalCau
8181
}
8282

8383
private static boolean isConnectionResetException(Throwable originalCause) {
84-
return originalCause instanceof IOException && originalCause.getMessage().contains("Connection reset by peer");
84+
String message = originalCause.getMessage();
85+
return originalCause instanceof IOException &&
86+
message != null &&
87+
message.contains("Connection reset by peer");
8588
}
8689

8790
private static boolean isAcquireTimeoutException(Throwable originalCause) {
@@ -95,7 +98,7 @@ private static boolean isTooManyPendingAcquiresException(Throwable originalCause
9598
String message = originalCause.getMessage();
9699
return originalCause instanceof IllegalStateException &&
97100
message != null &&
98-
originalCause.getMessage().contains("Too many outstanding acquire operations");
101+
message.contains("Too many outstanding acquire operations");
99102
}
100103

101104
private static String getMessageForAcquireTimeoutException() {

http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtilsTest.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@
2929
import io.netty.handler.ssl.SslContext;
3030
import io.netty.handler.ssl.SslContextBuilder;
3131
import io.netty.handler.ssl.SslHandler;
32+
import io.netty.handler.timeout.ReadTimeoutException;
33+
import io.netty.handler.timeout.WriteTimeoutException;
3234
import io.netty.util.Attribute;
3335
import io.netty.util.AttributeKey;
3436
import io.netty.util.concurrent.EventExecutor;
3537
import io.netty.util.concurrent.Future;
3638
import io.netty.util.concurrent.GenericFutureListener;
3739
import io.netty.util.concurrent.Promise;
40+
import java.io.IOException;
41+
import java.nio.channels.ClosedChannelException;
3842
import java.time.Duration;
43+
import java.util.concurrent.TimeoutException;
3944
import java.util.concurrent.atomic.AtomicBoolean;
4045
import java.util.concurrent.atomic.AtomicReference;
4146
import javax.net.ssl.SSLEngine;
@@ -268,4 +273,109 @@ public void closedChannelMessage_with_nullParentChannelAttribute() throws Except
268273
assertThat(NettyUtils.closedChannelMessage(channel))
269274
.isEqualTo(NettyUtils.CLOSED_CHANNEL_ERROR_MESSAGE);
270275
}
276+
277+
@Test
278+
public void decorateException_with_TimeoutException() {
279+
280+
Channel channel = mock(Channel.class);
281+
Throwable timeoutException = new TimeoutException("...Acquire operation took longer...");
282+
Throwable output = NettyUtils.decorateException(channel, timeoutException);
283+
284+
assertThat(output).isInstanceOf(Throwable.class);
285+
assertThat(output.getCause()).isInstanceOf(TimeoutException.class);
286+
assertThat(output.getMessage()).isNotNull();
287+
}
288+
289+
@Test
290+
public void decorateException_with_TimeoutException_noMsg() {
291+
292+
Channel channel = mock(Channel.class);
293+
Throwable timeoutException = new TimeoutException();
294+
Throwable output = NettyUtils.decorateException(channel, timeoutException);
295+
296+
assertThat(output).isInstanceOf(TimeoutException.class);
297+
assertThat(output.getCause()).isNull();
298+
}
299+
300+
@Test
301+
public void decorateException_with_IllegalStateException() {
302+
303+
Channel channel = mock(Channel.class);
304+
Throwable illegalStateException = new IllegalStateException("...Too many outstanding acquire operations...");
305+
Throwable output = NettyUtils.decorateException(channel, illegalStateException);
306+
307+
assertThat(output).isInstanceOf(Throwable.class);
308+
assertThat(output.getCause()).isInstanceOf(IllegalStateException.class);
309+
assertThat(output.getMessage()).isNotNull();
310+
}
311+
312+
@Test
313+
public void decorateException_with_IllegalStateException_noMsg() {
314+
315+
Channel channel = mock(Channel.class);
316+
Throwable illegalStateException = new IllegalStateException();
317+
Throwable output = NettyUtils.decorateException(channel, illegalStateException);
318+
319+
assertThat(output).isInstanceOf(IllegalStateException.class);
320+
assertThat(output.getCause()).isNull();
321+
}
322+
323+
@Test
324+
public void decorateException_with_ReadTimeoutException() {
325+
326+
Channel channel = mock(Channel.class);
327+
Throwable readTimeoutException = new ReadTimeoutException();
328+
Throwable output = NettyUtils.decorateException(channel, readTimeoutException);
329+
330+
assertThat(output).isInstanceOf(IOException.class);
331+
assertThat(output.getCause()).isInstanceOf(ReadTimeoutException.class);
332+
assertThat(output.getMessage()).isNotNull();
333+
}
334+
335+
@Test
336+
public void decorateException_with_WriteTimeoutException() {
337+
338+
Channel channel = mock(Channel.class);
339+
Throwable writeTimeoutException = new WriteTimeoutException();
340+
Throwable output = NettyUtils.decorateException(channel, writeTimeoutException);
341+
342+
assertThat(output).isInstanceOf(IOException.class);
343+
assertThat(output.getCause()).isInstanceOf(WriteTimeoutException.class);
344+
assertThat(output.getMessage()).isNotNull();
345+
}
346+
347+
@Test
348+
public void decorateException_with_ClosedChannelException() {
349+
350+
Channel channel = mock(Channel.class);
351+
Throwable closedChannelException = new ClosedChannelException();
352+
Throwable output = NettyUtils.decorateException(channel, closedChannelException);
353+
354+
assertThat(output).isInstanceOf(IOException.class);
355+
assertThat(output.getCause()).isInstanceOf(ClosedChannelException.class);
356+
assertThat(output.getMessage()).isNotNull();
357+
}
358+
359+
@Test
360+
public void decorateException_with_IOException_reset() {
361+
362+
Channel channel = mock(Channel.class);
363+
Throwable closedChannelException = new IOException("...Connection reset by peer...");
364+
Throwable output = NettyUtils.decorateException(channel, closedChannelException);
365+
366+
assertThat(output).isInstanceOf(IOException.class);
367+
assertThat(output.getCause()).isInstanceOf(IOException.class);
368+
assertThat(output.getMessage()).isNotNull();
369+
}
370+
371+
@Test
372+
public void decorateException_with_IOException_noMsg() {
373+
374+
Channel channel = mock(Channel.class);
375+
Throwable closedChannelException = new IOException();
376+
Throwable output = NettyUtils.decorateException(channel, closedChannelException);
377+
378+
assertThat(output).isInstanceOf(IOException.class);
379+
assertThat(output.getCause()).isNull();
380+
}
271381
}

0 commit comments

Comments
 (0)