Skip to content

Commit af60e6f

Browse files
committed
Fix OneTimeReadTimeoutHandler behavior
- Extend ReadTimeoutHandler for timeout behavior - Remove self from pipeline once a message is read
1 parent 691eca8 commit af60e6f

File tree

4 files changed

+22
-41
lines changed

4 files changed

+22
-41
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "Netty NIO HTTP Client",
3+
"type": "bugfix",
4+
"description": "Fix a bug in the Netty client where the read timeout isn't applied correctly for some requests."
5+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import java.io.IOException;
4848
import java.net.URI;
4949
import java.nio.ByteBuffer;
50+
import java.time.Duration;
5051
import java.util.Optional;
5152
import java.util.concurrent.CompletableFuture;
5253
import java.util.concurrent.TimeUnit;
@@ -210,8 +211,8 @@ private void writeRequest(HttpRequest request) {
210211

211212
// Should only add an one-time ReadTimeoutHandler to 100 Continue request.
212213
if (is100ContinueExpected()) {
213-
channel.pipeline().addFirst(new OneTimeReadTimeoutHandler(context.configuration().readTimeoutMillis(),
214-
TimeUnit.MILLISECONDS));
214+
channel.pipeline().addFirst(new OneTimeReadTimeoutHandler(Duration.ofMillis(context.configuration()
215+
.readTimeoutMillis())));
215216
} else {
216217
channel.pipeline().addFirst(new ReadTimeoutHandler(context.configuration().readTimeoutMillis(),
217218
TimeUnit.MILLISECONDS));

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

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,24 @@
1616
package software.amazon.awssdk.http.nio.netty.internal;
1717

1818
import io.netty.channel.ChannelHandlerContext;
19-
import io.netty.channel.SimpleChannelInboundHandler;
2019
import io.netty.handler.timeout.ReadTimeoutHandler;
21-
import io.netty.util.ReferenceCountUtil;
20+
import java.time.Duration;
2221
import java.util.concurrent.TimeUnit;
2322
import software.amazon.awssdk.annotations.SdkInternalApi;
2423

2524
/**
26-
* Handler to add an one-time {@link ReadTimeoutHandler} to the pipeline and remove it afterwards.
25+
* A one-time read timeout handler that removes itself from the pipeline after
26+
* the next successful read.
2727
*/
2828
@SdkInternalApi
29-
public final class OneTimeReadTimeoutHandler extends SimpleChannelInboundHandler {
30-
31-
private static final String READ_TIMEOUT_HANDLER_NAME = "RemoveAfterReadTimeoutHandler";
32-
private final long readTimeoutMillis;
33-
private final TimeUnit timeUnit;
34-
35-
OneTimeReadTimeoutHandler(long readTimeoutMillis, TimeUnit timeUnit) {
36-
this.readTimeoutMillis = readTimeoutMillis;
37-
this.timeUnit = timeUnit;
29+
public final class OneTimeReadTimeoutHandler extends ReadTimeoutHandler {
30+
OneTimeReadTimeoutHandler(Duration timeout) {
31+
super(timeout.toMillis(), TimeUnit.MILLISECONDS);
3832
}
3933

4034
@Override
41-
public void channelRead0(ChannelHandlerContext ctx, Object msg) {
42-
ReferenceCountUtil.retain(msg);
43-
ctx.pipeline().addFirst(READ_TIMEOUT_HANDLER_NAME, new ReadTimeoutHandler(readTimeoutMillis, timeUnit));
44-
ctx.fireChannelRead(msg);
45-
46-
ctx.pipeline().remove(READ_TIMEOUT_HANDLER_NAME);
35+
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
4736
ctx.pipeline().remove(this);
37+
super.channelRead(ctx, msg);
4838
}
4939
}

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

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,17 @@
1515

1616
package software.amazon.awssdk.http.nio.netty.internal;
1717

18-
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.Matchers.eq;
1919
import static org.mockito.Mockito.times;
2020
import static org.mockito.Mockito.verify;
2121
import static org.mockito.Mockito.when;
2222

2323
import io.netty.channel.ChannelHandlerContext;
2424
import io.netty.channel.ChannelPipeline;
25-
import io.netty.handler.timeout.ReadTimeoutHandler;
26-
import java.util.concurrent.TimeUnit;
25+
import java.time.Duration;
2726
import org.junit.BeforeClass;
2827
import org.junit.Test;
2928
import org.junit.runner.RunWith;
30-
import org.mockito.ArgumentCaptor;
3129
import org.mockito.Mock;
3230
import org.mockito.runners.MockitoJUnitRunner;
3331

@@ -48,29 +46,16 @@ public class OneTimeReadTimeoutHandlerTest {
4846

4947
@BeforeClass
5048
public static void setup() {
51-
handler = new OneTimeReadTimeoutHandler(TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS);
49+
handler = new OneTimeReadTimeoutHandler(Duration.ofMillis(TIMEOUT_IN_MILLIS));
5250
}
5351

5452
@Test
55-
public void channelRead_shouldAddReadTimeoutHandlerBeforeRead() {
56-
ArgumentCaptor<ReadTimeoutHandler> argumentCaptor = ArgumentCaptor.forClass(ReadTimeoutHandler.class);
57-
ArgumentCaptor<String> handlerNameCaptor = ArgumentCaptor.forClass(String.class);
58-
53+
public void channelRead_removesSelf() throws Exception {
5954
when(context.pipeline()).thenReturn(channelPipeline);
6055

61-
handler.channelRead0(context, object);
62-
63-
verify(channelPipeline, times(1)).addFirst(handlerNameCaptor.capture(),
64-
argumentCaptor.capture());
56+
handler.channelRead(context, object);
6557

58+
verify(channelPipeline, times(1)).remove(eq(handler));
6659
verify(context, times(1)).fireChannelRead(object);
67-
68-
ReadTimeoutHandler readTimeoutHandler = argumentCaptor.getValue();
69-
assertThat(readTimeoutHandler.getReaderIdleTimeInMillis()).isEqualTo(TIMEOUT_IN_MILLIS);
70-
71-
verify(channelPipeline, times(1)).remove(handlerNameCaptor.getValue());
72-
73-
verify(channelPipeline, times(1)).remove(handler);
74-
7560
}
7661
}

0 commit comments

Comments
 (0)