Skip to content

Commit 1c8ae14

Browse files
committed
Fix race codition between read and finalizeRequest
HttpStreamsClientHandler ignores the LastHttpContent decoded by the HTTP codec when we get an empty response from the service, but if the channel gets released (which causes the streams handler to get removed) before the last content message comes through and is ignored by the handler, the new HttpStreamsHandler (added by RunnableRquest) will not be in a state to anticipate and ignore the LastHttpContent and will instead do the wrong thing and attempt to remove a non-existent handler. This commit fixes that race condition by explicitly replacing the HttpStreamsClientHandler in the pipeline with a new handler that ignores the next message read in the pipeline if it's a LastHttpContent. Fixes #202
1 parent cf9c41a commit 1c8ae14

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
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 Async HTTP Client",
3+
"type": "bugfix",
4+
"description": "Fix race condition in the async client causing instability when making multiple concurent requests. Fixes [#202](https://github.com/aws/aws-sdk-java-v2/issues/202)"
5+
}

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKeys.REQUEST_CONTEXT_KEY;
2121
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKeys.RESPONSE_COMPLETE_KEY;
2222

23+
import com.typesafe.netty.http.HttpStreamsClientHandler;
2324
import com.typesafe.netty.http.StreamedHttpResponse;
2425
import io.netty.buffer.ByteBuf;
2526
import io.netty.channel.ChannelHandler.Sharable;
@@ -31,6 +32,7 @@
3132
import io.netty.handler.codec.http.HttpObject;
3233
import io.netty.handler.codec.http.HttpResponse;
3334
import io.netty.handler.codec.http.HttpUtil;
35+
import io.netty.handler.codec.http.LastHttpContent;
3436
import io.netty.util.AttributeKey;
3537
import java.io.IOException;
3638
import java.nio.ByteBuffer;
@@ -75,12 +77,10 @@ protected void channelRead0(ChannelHandlerContext channelContext, HttpObject msg
7577
if (msg instanceof StreamedHttpResponse) {
7678
requestContext.handler().onStream(new PublisherAdapter((StreamedHttpResponse) msg, channelContext, requestContext));
7779
} else if (msg instanceof FullHttpResponse) {
78-
// TODO: HttpStreamsClientHandler leaves a dangling LastHttpContent
79-
// in the pipeline. Consume it ourselves to make sure the channel
80-
// is empty at the end of stream and before releasing it back to he
81-
// pool. The HttpStreamsClientHandler should really be doing this
82-
// for us.
83-
channelContext.read();
80+
// Be prepared to take care of (ignore) a trailing LastHttpResponse
81+
// from the HttpClientCodec if there is one.
82+
channelContext.pipeline().replace(HttpStreamsClientHandler.class,
83+
channelContext.name() + "-LastHttpContentSwallower", new LastHttpContentSwallower());
8484

8585
ByteBuf fullContent = ((FullHttpResponse) msg).content();
8686
final ByteBuffer bb = copyToByteBuffer(fullContent);
@@ -239,4 +239,20 @@ public void subscribe(Subscriber<? super ByteBuffer> subscriber) {
239239
.set(subscriber);
240240
}
241241
}
242+
243+
244+
private static class LastHttpContentSwallower extends SimpleChannelInboundHandler<HttpObject> {
245+
@Override
246+
protected void channelRead0(ChannelHandlerContext ctx, HttpObject obj) throws Exception {
247+
if (obj instanceof LastHttpContent) {
248+
// Queue another read to make up for the one we just ignored
249+
ctx.read();
250+
} else {
251+
ctx.fireChannelRead(obj);
252+
}
253+
// Remove self from pipeline since we only care about potentially
254+
// ignoring the very first message
255+
ctx.pipeline().remove(this);
256+
}
257+
}
242258
}

0 commit comments

Comments
 (0)