1
15
16 package software.amazon.awssdk.core.internal.http;
17
18 import java.io.IOException;
19 import java.util.Optional;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22 import software.amazon.awssdk.annotations.SdkInternalApi;
23 import software.amazon.awssdk.core.Response;
24 import software.amazon.awssdk.core.SdkStandardLogger;
25 import software.amazon.awssdk.core.exception.RetryableException;
26 import software.amazon.awssdk.core.exception.SdkClientException;
27 import software.amazon.awssdk.core.exception.SdkException;
28 import software.amazon.awssdk.core.http.HttpResponseHandler;
29 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
30 import software.amazon.awssdk.http.SdkHttpFullResponse;
31 import software.amazon.awssdk.utils.IoUtils;
32
33
40 @SdkInternalApi
41 public class CombinedResponseHandler<OutputT> implements HttpResponseHandler<Response<OutputT>> {
42 private static final Logger log = LoggerFactory.getLogger(CombinedResponseHandler.class);
43
44 private final HttpResponseHandler<OutputT> successResponseHandler;
45 private final HttpResponseHandler<? extends SdkException> errorResponseHandler;
46
47 public CombinedResponseHandler(HttpResponseHandler<OutputT> successResponseHandler,
48 HttpResponseHandler<? extends SdkException> errorResponseHandler) {
49 this.successResponseHandler = successResponseHandler;
50 this.errorResponseHandler = errorResponseHandler;
51 }
52
53 @Override
54 public Response<OutputT> handle(SdkHttpFullResponse httpResponse, ExecutionAttributes executionAttributes)
55 throws Exception {
56
57 boolean didRequestFail = true;
58 try {
59 Response<OutputT> response = handleResponse(httpResponse, executionAttributes);
60 didRequestFail = !response.isSuccess();
61 return response;
62 } finally {
63 closeInputStreamIfNeeded(httpResponse, didRequestFail);
64 }
65 }
66
67 private Response<OutputT> handleResponse(SdkHttpFullResponse httpResponse,
68 ExecutionAttributes executionAttributes)
69 throws IOException, InterruptedException {
70
71 if (httpResponse.isSuccessful()) {
72 OutputT response = handleSuccessResponse(httpResponse, executionAttributes);
73 return Response.<OutputT>builder().httpResponse(httpResponse)
74 .response(response)
75 .isSuccess(true)
76 .build();
77 } else {
78 return Response.<OutputT>builder().httpResponse(httpResponse)
79 .exception(handleErrorResponse(httpResponse, executionAttributes))
80 .isSuccess(false)
81 .build();
82 }
83 }
84
85
93 private OutputT handleSuccessResponse(SdkHttpFullResponse httpResponse, ExecutionAttributes executionAttributes)
94 throws IOException, InterruptedException {
95 try {
96 SdkStandardLogger.REQUEST_LOGGER.debug(() -> "Received successful response: " + httpResponse.statusCode());
97 return successResponseHandler.handle(httpResponse, executionAttributes);
98 } catch (IOException | InterruptedException | RetryableException e) {
99 throw e;
100 } catch (Exception e) {
101 if (e instanceof SdkException && ((SdkException) e).retryable()) {
102 throw (SdkException) e;
103 }
104
105 String errorMessage =
106 "Unable to unmarshall response (" + e.getMessage() + "). Response Code: "
107 + httpResponse.statusCode() + ", Response Text: " + httpResponse.statusText().orElse(null);
108 throw SdkClientException.builder().message(errorMessage).cause(e).build();
109 }
110 }
111
112
118 private SdkException handleErrorResponse(SdkHttpFullResponse httpResponse,
119 ExecutionAttributes executionAttributes)
120 throws IOException, InterruptedException {
121 try {
122 SdkException exception = errorResponseHandler.handle(httpResponse, executionAttributes);
123 exception.fillInStackTrace();
124 SdkStandardLogger.REQUEST_LOGGER.debug(() -> "Received error response: " + exception);
125 return exception;
126 } catch (InterruptedException | IOException e) {
127 throw e;
128 } catch (Exception e) {
129 String errorMessage = String.format("Unable to unmarshall error response (%s). " +
130 "Response Code: %d, Response Text: %s", e.getMessage(),
131 httpResponse.statusCode(), httpResponse.statusText().orElse("null"));
132 throw SdkClientException.builder().message(errorMessage).cause(e).build();
133 }
134 }
135
136
139 private void closeInputStreamIfNeeded(SdkHttpFullResponse httpResponse,
140 boolean didRequestFail) {
141
142 if (didRequestFail || !successResponseHandler.needsConnectionLeftOpen()) {
143 Optional.ofNullable(httpResponse)
144 .flatMap(SdkHttpFullResponse::content)
145 .ifPresent(s -> IoUtils.closeQuietly(s, log));
146 }
147 }
148 }
149