Skip to content

Commit a5e6761

Browse files
authored
Merge pull request #1905 from aws/millem/fix-release
Revert "Validate that the response content-length header matches what…
2 parents de3fb1f + 6d93c8e commit a5e6761

File tree

12 files changed

+132
-799
lines changed

12 files changed

+132
-799
lines changed

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ public final class ChannelAttributeKey {
7070
public static final AttributeKey<Http2FrameStream> HTTP2_FRAME_STREAM = NettyUtils.getOrCreateAttributeKey(
7171
"aws.http.nio.netty.async.http2FrameStream");
7272

73-
public static final AttributeKey<ChannelDiagnostics> CHANNEL_DIAGNOSTICS = NettyUtils.getOrCreateAttributeKey(
74-
"aws.http.nio.netty.async.channelDiagnostics");
75-
7673
/**
7774
* {@link AttributeKey} to keep track of whether we should close the connection after this request
7875
* has completed.
@@ -91,12 +88,6 @@ public final class ChannelAttributeKey {
9188
static final AttributeKey<Boolean> RESPONSE_COMPLETE_KEY = NettyUtils.getOrCreateAttributeKey(
9289
"aws.http.nio.netty.async.responseComplete");
9390

94-
static final AttributeKey<Long> RESPONSE_CONTENT_LENGTH = NettyUtils.getOrCreateAttributeKey(
95-
"aws.http.nio.netty.async.responseContentLength");
96-
97-
static final AttributeKey<Long> RESPONSE_DATA_READ = NettyUtils.getOrCreateAttributeKey(
98-
"aws.http.nio.netty.async.responseDataRead");
99-
10091
/**
10192
* {@link AttributeKey} to keep track of whether we have received the {@link LastHttpContent}.
10293
*/

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

Lines changed: 0 additions & 50 deletions
This file was deleted.

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

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

18-
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_DIAGNOSTICS;
1918
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_CONNECTION;
2019
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_INITIAL_WINDOW_SIZE;
2120
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE;
@@ -89,7 +88,6 @@ public ChannelPipelineInitializer(Protocol protocol,
8988

9089
@Override
9190
public void channelCreated(Channel ch) {
92-
ch.attr(CHANNEL_DIAGNOSTICS).set(new ChannelDiagnostics(ch));
9391
ch.attr(PROTOCOL_FUTURE).set(new CompletableFuture<>());
9492
ChannelPipeline pipeline = ch.pipeline();
9593
if (sslCtx != null) {

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

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

1818
import static software.amazon.awssdk.http.HttpMetric.CONCURRENCY_ACQUIRE_DURATION;
19-
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_DIAGNOSTICS;
2019
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.EXECUTE_FUTURE_KEY;
2120
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.EXECUTION_ID_KEY;
2221
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.IN_USE;
2322
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.KEEP_ALIVE;
2423
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.LAST_HTTP_CONTENT_RECEIVED_KEY;
2524
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.REQUEST_CONTEXT_KEY;
2625
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.RESPONSE_COMPLETE_KEY;
27-
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.RESPONSE_CONTENT_LENGTH;
28-
import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.RESPONSE_DATA_READ;
2926
import static software.amazon.awssdk.http.nio.netty.internal.NettyRequestMetrics.measureTimeTaken;
27+
import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.CLOSED_CHANNEL_MESSAGE;
3028

3129
import io.netty.buffer.ByteBuf;
3230
import io.netty.buffer.Unpooled;
@@ -40,18 +38,22 @@
4038
import io.netty.handler.codec.http.HttpMethod;
4139
import io.netty.handler.codec.http.HttpRequest;
4240
import io.netty.handler.codec.http.HttpVersion;
41+
import io.netty.handler.timeout.ReadTimeoutException;
4342
import io.netty.handler.timeout.ReadTimeoutHandler;
43+
import io.netty.handler.timeout.WriteTimeoutException;
4444
import io.netty.handler.timeout.WriteTimeoutHandler;
4545
import io.netty.util.concurrent.Future;
4646
import io.netty.util.concurrent.GenericFutureListener;
4747
import io.netty.util.concurrent.Promise;
4848
import java.io.IOException;
4949
import java.net.URI;
5050
import java.nio.ByteBuffer;
51+
import java.nio.channels.ClosedChannelException;
5152
import java.time.Duration;
5253
import java.util.Optional;
5354
import java.util.concurrent.CompletableFuture;
5455
import java.util.concurrent.TimeUnit;
56+
import java.util.concurrent.TimeoutException;
5557
import java.util.concurrent.atomic.AtomicLong;
5658
import java.util.function.Supplier;
5759
import org.reactivestreams.Publisher;
@@ -174,13 +176,9 @@ private void makeRequestListener(Future<Channel> channelFuture) {
174176
if (channelFuture.isSuccess()) {
175177
channel = channelFuture.getNow();
176178
NettyUtils.doInEventLoop(channel.eventLoop(), () -> {
177-
try {
178-
configureChannel();
179-
configurePipeline();
179+
configureChannel();
180+
if (tryConfigurePipeline()) {
180181
makeRequest();
181-
} catch (Throwable t) {
182-
closeAndRelease(channel);
183-
handleFailure(channel, () -> "Failed to initiate request to " + endpoint(), t);
184182
}
185183
});
186184
} else {
@@ -194,13 +192,10 @@ private void configureChannel() {
194192
channel.attr(REQUEST_CONTEXT_KEY).set(context);
195193
channel.attr(RESPONSE_COMPLETE_KEY).set(false);
196194
channel.attr(LAST_HTTP_CONTENT_RECEIVED_KEY).set(false);
197-
channel.attr(RESPONSE_CONTENT_LENGTH).set(null);
198-
channel.attr(RESPONSE_DATA_READ).set(null);
199-
channel.attr(CHANNEL_DIAGNOSTICS).get().incrementRequestCount();
200195
channel.config().setOption(ChannelOption.AUTO_READ, false);
201196
}
202197

203-
private void configurePipeline() throws IOException {
198+
private boolean tryConfigurePipeline() {
204199
Protocol protocol = ChannelAttributeKey.getProtocolNow(channel);
205200
ChannelPipeline pipeline = channel.pipeline();
206201

@@ -215,7 +210,10 @@ private void configurePipeline() throws IOException {
215210
requestAdapter = REQUEST_ADAPTER_HTTP1_1;
216211
break;
217212
default:
218-
throw new IOException("Unknown protocol: " + protocol);
213+
String errorMsg = "Unknown protocol: " + protocol;
214+
closeAndRelease(channel);
215+
handleFailure(channel, () -> errorMsg, new RuntimeException(errorMsg));
216+
return false;
219217
}
220218

221219
pipeline.addLast(LastHttpContentHandler.create());
@@ -229,8 +227,13 @@ private void configurePipeline() throws IOException {
229227
// handler (which will monitor for it going inactive from now on).
230228
// Make sure it's active here, or the request will never complete: https://github.com/aws/aws-sdk-java-v2/issues/1207
231229
if (!channel.isActive()) {
232-
throw new IOException(NettyUtils.closedChannelMessage(channel));
230+
String errorMessage = "Channel was closed before it could be written to.";
231+
closeAndRelease(channel);
232+
handleFailure(channel, () -> errorMessage, new IOException(errorMessage));
233+
return false;
233234
}
235+
236+
return true;
234237
}
235238

236239
private void makeRequest() {
@@ -305,11 +308,80 @@ private URI endpoint() {
305308

306309
private void handleFailure(Channel channel, Supplier<String> msgSupplier, Throwable cause) {
307310
log.debug(channel, msgSupplier, cause);
308-
cause = NettyUtils.decorateException(channel, cause);
311+
cause = decorateException(cause);
309312
context.handler().onError(cause);
310313
executeFuture.completeExceptionally(cause);
311314
}
312315

316+
private Throwable decorateException(Throwable originalCause) {
317+
if (isAcquireTimeoutException(originalCause)) {
318+
return new Throwable(getMessageForAcquireTimeoutException(), originalCause);
319+
} else if (isTooManyPendingAcquiresException(originalCause)) {
320+
return new Throwable(getMessageForTooManyAcquireOperationsError(), originalCause);
321+
} else if (originalCause instanceof ReadTimeoutException) {
322+
return new IOException("Read timed out", originalCause);
323+
} else if (originalCause instanceof WriteTimeoutException) {
324+
return new IOException("Write timed out", originalCause);
325+
} else if (originalCause instanceof ClosedChannelException) {
326+
return new IOException(CLOSED_CHANNEL_MESSAGE, originalCause);
327+
}
328+
329+
return originalCause;
330+
}
331+
332+
private boolean isAcquireTimeoutException(Throwable originalCause) {
333+
String message = originalCause.getMessage();
334+
return originalCause instanceof TimeoutException &&
335+
message != null &&
336+
message.contains("Acquire operation took longer");
337+
}
338+
339+
private boolean isTooManyPendingAcquiresException(Throwable originalCause) {
340+
String message = originalCause.getMessage();
341+
return originalCause instanceof IllegalStateException &&
342+
message != null &&
343+
originalCause.getMessage().contains("Too many outstanding acquire operations");
344+
}
345+
346+
private String getMessageForAcquireTimeoutException() {
347+
return "Acquire operation took longer than the configured maximum time. This indicates that a request cannot get a "
348+
+ "connection from the pool within the specified maximum time. This can be due to high request rate.\n"
349+
350+
+ "Consider taking any of the following actions to mitigate the issue: increase max connections, "
351+
+ "increase acquire timeout, or slowing the request rate.\n"
352+
353+
+ "Increasing the max connections can increase client throughput (unless the network interface is already "
354+
+ "fully utilized), but can eventually start to hit operation system limitations on the number of file "
355+
+ "descriptors used by the process. If you already are fully utilizing your network interface or cannot "
356+
+ "further increase your connection count, increasing the acquire timeout gives extra time for requests to "
357+
+ "acquire a connection before timing out. If the connections doesn't free up, the subsequent requests "
358+
+ "will still timeout.\n"
359+
360+
+ "If the above mechanisms are not able to fix the issue, try smoothing out your requests so that large "
361+
+ "traffic bursts cannot overload the client, being more efficient with the number of times you need to "
362+
+ "call AWS, or by increasing the number of hosts sending requests.";
363+
}
364+
365+
private String getMessageForTooManyAcquireOperationsError() {
366+
return "Maximum pending connection acquisitions exceeded. The request rate is too high for the client to keep up.\n"
367+
368+
+ "Consider taking any of the following actions to mitigate the issue: increase max connections, "
369+
+ "increase max pending acquire count, decrease pool lease timeout, or slowing the request rate.\n"
370+
371+
+ "Increasing the max connections can increase client throughput (unless the network interface is already "
372+
+ "fully utilized), but can eventually start to hit operation system limitations on the number of file "
373+
+ "descriptors used by the process. If you already are fully utilizing your network interface or cannot "
374+
+ "further increase your connection count, increasing the pending acquire count allows extra requests to be "
375+
+ "buffered by the client, but can cause additional request latency and higher memory usage. If your request"
376+
+ " latency or memory usage is already too high, decreasing the lease timeout will allow requests to fail "
377+
+ "more quickly, reducing the number of pending connection acquisitions, but likely won't decrease the total "
378+
+ "number of failed requests.\n"
379+
380+
+ "If the above mechanisms are not able to fix the issue, try smoothing out your requests so that large "
381+
+ "traffic bursts cannot overload the client, being more efficient with the number of times you need to call "
382+
+ "AWS, or by increasing the number of hosts sending requests.";
383+
}
384+
313385
/**
314386
* Close and release the channel back to the pool.
315387
*

0 commit comments

Comments
 (0)