Skip to content

Commit f0842ae

Browse files
committed
AWS-XML: Support for handling error in response body with success HTTP code for all non-streaming requests
1 parent 18cefdc commit f0842ae

File tree

72 files changed

+3087
-713
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+3087
-713
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "Amazon S3",
4+
"description": "Requests that return an error response in the body of the HTTP response with a successful (200) status code will now correctly be handled as a failed request by the SDK."
5+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
194194
.beginControlFlow("try")
195195
.addCode(ClientClassUtils.callApplySignerOverrideMethod(opModel))
196196
.addCode(ClientClassUtils.addEndpointTraitCode(opModel))
197-
.addCode(protocolSpec.responseHandler(model, opModel))
198-
.addCode(protocolSpec.errorResponseHandler(opModel))
199-
.addCode(eventToByteBufferPublisher(opModel));
197+
.addCode(protocolSpec.responseHandler(model, opModel));
198+
protocolSpec.errorResponseHandler(opModel).ifPresent(builder::addCode);
199+
builder.addCode(eventToByteBufferPublisher(opModel));
200200

201201
if (opModel.getEndpointDiscovery() != null) {
202202
builder.addStatement("$T cachedEndpoint = null", URI.class);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,9 @@ private List<MethodSpec> operationMethodSpecs(OperationModel opModel) {
167167
.addAnnotation(Override.class)
168168
.addCode(ClientClassUtils.callApplySignerOverrideMethod(opModel))
169169
.addCode(ClientClassUtils.addEndpointTraitCode(opModel))
170-
.addCode(protocolSpec.responseHandler(model, opModel))
171-
.addCode(protocolSpec.errorResponseHandler(opModel));
170+
.addCode(protocolSpec.responseHandler(model, opModel));
171+
172+
protocolSpec.errorResponseHandler(opModel).ifPresent(method::addCode);
172173

173174
if (opModel.getEndpointDiscovery() != null) {
174175
method.addStatement("$T cachedEndpoint = null", URI.class);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/JsonProtocolSpec.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,14 @@ public CodeBlock responseHandler(IntermediateModel model, OperationModel opModel
143143
}
144144

145145
@Override
146-
public CodeBlock errorResponseHandler(OperationModel opModel) {
146+
public Optional<CodeBlock> errorResponseHandler(OperationModel opModel) {
147147
String protocolFactory = protocolFactoryLiteral(model, opModel);
148148

149-
return CodeBlock
150-
.builder()
151-
.add("\n\n$T<$T> errorResponseHandler = createErrorResponseHandler($L, operationMetadata);",
152-
HttpResponseHandler.class, AwsServiceException.class, protocolFactory)
153-
.build();
149+
return Optional.of(
150+
CodeBlock.builder()
151+
.add("\n\n$T<$T> errorResponseHandler = createErrorResponseHandler($L, operationMetadata);",
152+
HttpResponseHandler.class, AwsServiceException.class, protocolFactory)
153+
.build());
154154
}
155155

156156
@Override

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/ProtocolSpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public interface ProtocolSpec {
4646

4747
CodeBlock responseHandler(IntermediateModel model, OperationModel opModel);
4848

49-
CodeBlock errorResponseHandler(OperationModel opModel);
49+
Optional<CodeBlock> errorResponseHandler(OperationModel opModel);
5050

5151
CodeBlock executionHandler(OperationModel opModel);
5252

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/QueryProtocolSpec.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
public class QueryProtocolSpec implements ProtocolSpec {
3636

3737
protected final PoetExtensions poetExtensions;
38-
private final IntermediateModel intermediateModel;
38+
protected final IntermediateModel intermediateModel;
3939

4040
public QueryProtocolSpec(IntermediateModel intermediateModel, PoetExtensions poetExtensions) {
4141
this.intermediateModel = intermediateModel;
@@ -84,10 +84,12 @@ public CodeBlock responseHandler(IntermediateModel model,
8484
}
8585

8686
@Override
87-
public CodeBlock errorResponseHandler(OperationModel opModel) {
88-
return CodeBlock.builder().add("\n\n$T errorResponseHandler = protocolFactory.createErrorResponseHandler();",
89-
ParameterizedTypeName.get(HttpResponseHandler.class, AwsServiceException.class))
90-
.build();
87+
public Optional<CodeBlock> errorResponseHandler(OperationModel opModel) {
88+
return Optional.of(
89+
CodeBlock.builder()
90+
.add("\n\n$T errorResponseHandler = protocolFactory.createErrorResponseHandler();",
91+
ParameterizedTypeName.get(HttpResponseHandler.class, AwsServiceException.class))
92+
.build());
9193
}
9294

9395
@Override

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/XmlProtocolSpec.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717

1818
import com.squareup.javapoet.ClassName;
1919
import com.squareup.javapoet.CodeBlock;
20+
import com.squareup.javapoet.ParameterizedTypeName;
21+
import com.squareup.javapoet.TypeName;
22+
23+
import java.util.Optional;
24+
2025
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2126
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
2227
import software.amazon.awssdk.codegen.poet.PoetExtensions;
@@ -49,6 +54,29 @@ protected Class<?> protocolFactoryClass() {
4954
@Override
5055
public CodeBlock responseHandler(IntermediateModel model,
5156
OperationModel opModel) {
57+
58+
if (opModel.hasStreamingOutput()) {
59+
return streamingResponseHandler(opModel);
60+
}
61+
62+
ClassName responseType = poetExtensions.getModelClass(opModel.getReturnType().getReturnType());
63+
64+
TypeName handlerType = ParameterizedTypeName.get(
65+
ClassName.get(HttpResponseHandler.class),
66+
ParameterizedTypeName.get(ClassName.get(software.amazon.awssdk.core.Response.class), responseType));
67+
68+
return CodeBlock.builder()
69+
.addStatement("\n\n$T responseHandler = protocolFactory.createCombinedResponseHandler"
70+
+ "($T::builder,"
71+
+ "new $T().withHasStreamingSuccessResponse($L))",
72+
handlerType,
73+
responseType,
74+
XmlOperationMetadata.class,
75+
opModel.hasStreamingOutput())
76+
.build();
77+
}
78+
79+
private CodeBlock streamingResponseHandler(OperationModel opModel) {
5280
ClassName responseType = poetExtensions.getModelClass(opModel.getReturnType().getReturnType());
5381

5482
return CodeBlock.builder()
@@ -62,4 +90,92 @@ public CodeBlock responseHandler(IntermediateModel model,
6290
.build();
6391
}
6492

93+
@Override
94+
public Optional<CodeBlock> errorResponseHandler(OperationModel opModel) {
95+
return opModel.hasStreamingOutput() ? streamingErrorResponseHandler(opModel) : Optional.empty();
96+
}
97+
98+
private Optional<CodeBlock> streamingErrorResponseHandler(OperationModel opModel) {
99+
return super.errorResponseHandler(opModel);
100+
}
101+
102+
@Override
103+
public CodeBlock executionHandler(OperationModel opModel) {
104+
if (opModel.hasStreamingOutput()) {
105+
return streamingExecutionHandler(opModel);
106+
}
107+
108+
TypeName responseType = poetExtensions.getModelClass(opModel.getReturnType().getReturnType());
109+
ClassName requestType = poetExtensions.getModelClass(opModel.getInput().getVariableType());
110+
ClassName marshaller = poetExtensions.getTransformClass(opModel.getInputShape().getShapeName() + "Marshaller");
111+
CodeBlock.Builder codeBlock = CodeBlock
112+
.builder()
113+
.add("\n\nreturn clientHandler.execute(new $T<$T, $T>()" +
114+
".withOperationName(\"$N\")\n" +
115+
".withCombinedResponseHandler($N)" +
116+
hostPrefixExpression(opModel) +
117+
discoveredEndpoint(opModel) +
118+
".withInput($L)",
119+
software.amazon.awssdk.core.client.handler.ClientExecutionParams.class,
120+
requestType,
121+
responseType,
122+
opModel.getOperationName(),
123+
"responseHandler",
124+
opModel.getInput().getVariableName());
125+
if (opModel.hasStreamingInput()) {
126+
return codeBlock.add(".withRequestBody(requestBody)")
127+
.add(".withMarshaller($L));", syncStreamingMarshaller(intermediateModel, opModel, marshaller))
128+
.build();
129+
}
130+
return codeBlock.add(".withMarshaller(new $T(protocolFactory)) $L);", marshaller,
131+
opModel.hasStreamingOutput() ? ", responseTransformer" : "").build();
132+
}
133+
134+
private CodeBlock streamingExecutionHandler(OperationModel opModel) {
135+
return super.executionHandler(opModel);
136+
}
137+
138+
@Override
139+
public CodeBlock asyncExecutionHandler(IntermediateModel intermediateModel, OperationModel opModel) {
140+
if (opModel.hasStreamingOutput()) {
141+
return asyncStreamingExecutionHandler(intermediateModel, opModel);
142+
}
143+
144+
ClassName pojoResponseType = poetExtensions.getModelClass(opModel.getReturnType().getReturnType());
145+
ClassName requestType = poetExtensions.getModelClass(opModel.getInput().getVariableType());
146+
ClassName marshaller = poetExtensions.getRequestTransformClass(opModel.getInputShape().getShapeName() + "Marshaller");
147+
148+
String asyncRequestBody = opModel.hasStreamingInput() ? ".withAsyncRequestBody(requestBody)"
149+
: "";
150+
TypeName executeFutureValueType = executeFutureValueType(opModel, poetExtensions);
151+
CodeBlock.Builder builder =
152+
CodeBlock.builder().add("\n\n$T<$T> executeFuture = clientHandler.execute(new $T<$T, $T>()"
153+
+ "\n" +
154+
".withOperationName(\"$N\")\n" +
155+
".withMarshaller($L)" +
156+
".withCombinedResponseHandler($N)" +
157+
hostPrefixExpression(opModel) +
158+
asyncRequestBody +
159+
".withInput($L) $L);",
160+
java.util.concurrent.CompletableFuture.class,
161+
executeFutureValueType,
162+
software.amazon.awssdk.core.client.handler.ClientExecutionParams.class,
163+
requestType,
164+
pojoResponseType,
165+
opModel.getOperationName(),
166+
asyncMarshaller(intermediateModel, opModel, marshaller, "protocolFactory"),
167+
"responseHandler",
168+
opModel.getInput().getVariableName(),
169+
opModel.hasStreamingOutput() ? ", asyncResponseTransformer" : "");
170+
171+
if (opModel.hasStreamingOutput()) {
172+
builder.add("executeFuture$L;", streamingOutputWhenComplete("asyncResponseTransformer"));
173+
}
174+
builder.addStatement("return executeFuture");
175+
return builder.build();
176+
}
177+
178+
private CodeBlock asyncStreamingExecutionHandler(IntermediateModel intermediateModel, OperationModel opModel) {
179+
return super.asyncExecutionHandler(intermediateModel, opModel);
180+
}
65181
}

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsAsyncClientHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
2929
import software.amazon.awssdk.core.client.handler.SdkAsyncClientHandler;
3030
import software.amazon.awssdk.core.http.ExecutionContext;
31+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
3132

3233
/**
3334
* Async client handler for AWS SDK clients.
@@ -60,8 +61,8 @@ public <InputT extends SdkRequest, OutputT extends SdkResponse, ReturnT> Complet
6061

6162
@Override
6263
protected <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext createExecutionContext(
63-
ClientExecutionParams<InputT, OutputT> executionParams) {
64-
return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration);
64+
ClientExecutionParams<InputT, OutputT> executionParams, ExecutionAttributes executionAttributes) {
65+
return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration, executionAttributes);
6566
}
6667

6768
}

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ private AwsClientHandlerUtils() {
5858

5959
static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext createExecutionContext(
6060
ClientExecutionParams<InputT, OutputT> executionParams,
61-
SdkClientConfiguration clientConfig) {
61+
SdkClientConfiguration clientConfig,
62+
ExecutionAttributes executionAttributes) {
6263

6364
SdkRequest originalRequest = executionParams.getInput();
6465
AwsCredentialsProvider clientCredentials = clientConfig.option(AwsClientOption.CREDENTIALS_PROVIDER);
@@ -72,7 +73,7 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
7273

7374
Validate.validState(credentials != null, "Credential providers must never return null.");
7475

75-
ExecutionAttributes executionAttributes = new ExecutionAttributes()
76+
executionAttributes
7677
.putAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG, clientConfig.option(SdkClientOption.SERVICE_CONFIGURATION))
7778
.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, credentials)
7879
.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME,

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsSyncClientHandler.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,19 @@ public <InputT extends SdkRequest, OutputT extends SdkResponse, ReturnT> ReturnT
6464

6565
@Override
6666
protected <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext createExecutionContext(
67-
ClientExecutionParams<InputT, OutputT> executionParams) {
68-
return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration);
67+
ClientExecutionParams<InputT, OutputT> executionParams, ExecutionAttributes executionAttributes) {
68+
return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration, executionAttributes);
6969
}
7070

7171
private <InputT extends SdkRequest, OutputT> ClientExecutionParams<InputT, OutputT> addCrc32Validation(
7272
ClientExecutionParams<InputT, OutputT> executionParams) {
73-
return executionParams.withResponseHandler(new Crc32ValidationResponseHandler<>(executionParams.getResponseHandler()));
73+
if (executionParams.getCombinedResponseHandler() != null) {
74+
return executionParams.withCombinedResponseHandler(
75+
new Crc32ValidationResponseHandler<>(executionParams.getCombinedResponseHandler()));
76+
} else {
77+
return executionParams.withResponseHandler(
78+
new Crc32ValidationResponseHandler<>(executionParams.getResponseHandler()));
79+
}
7480
}
7581

7682
/**

0 commit comments

Comments
 (0)