1 /*
2  * Copyright 2011-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.http.response;
16
17 import com.amazonaws.AmazonWebServiceRequest;
18 import com.amazonaws.AmazonWebServiceResponse;
19 import com.amazonaws.AmazonWebServiceResult;
20 import com.amazonaws.Request;
21 import com.amazonaws.http.AmazonHttpClient;
22 import com.amazonaws.http.HttpResponse;
23 import com.amazonaws.http.HttpResponseHandler;
24 import com.amazonaws.http.SdkHttpMetadata;
25 import com.amazonaws.util.AWSRequestMetrics;
26 import com.amazonaws.util.MetadataCache;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 /**
31  * Adapts an {@link HttpResponseHandler < AmazonWebServiceResponse <T>>} to an {@link
32  * HttpResponseHandler<T>} (unwrapped result) with proper handling and logging of response
33  * metadata.
34  *
35  * @param <T> Unmarshalled result type
36  */

37 public class AwsResponseHandlerAdapter<T> implements HttpResponseHandler<T> {
38
39     /**
40      * Logger used for the purpose of logging the AWS request id extracted either from the
41      * httpClientSettings header response or from the response body.
42      */

43     private static final Log requestIdLog = LogFactory.getLog("com.amazonaws.requestId");
44
45     private static final Log requestLog = AmazonHttpClient.requestLog;
46
47     private final HttpResponseHandler<AmazonWebServiceResponse<T>> delegate;
48     private final Request<?> request;
49     private final AWSRequestMetrics awsRequestMetrics;
50     private final MetadataCache responseMetadataCache;
51
52     /**
53      * @param delegate          Response handler to delegate to and unwrap
54      * @param request           Marshalled request
55      * @param awsRequestMetrics Request metrics
56      */

57     public AwsResponseHandlerAdapter(HttpResponseHandler<AmazonWebServiceResponse<T>> delegate,
58                                      Request<?> request,
59                                      AWSRequestMetrics awsRequestMetrics,
60                                      MetadataCache responseMetadataCache) {
61         this.delegate = delegate;
62         this.request = request;
63         this.awsRequestMetrics = awsRequestMetrics;
64         this.responseMetadataCache = responseMetadataCache;
65     }
66
67     @Override
68     public T handle(HttpResponse response) throws Exception {
69         final AmazonWebServiceResponse<T> awsResponse = delegate.handle(response);
70
71         if (awsResponse == null) {
72             throw new RuntimeException("Unable to unmarshall response metadata. Response Code: "
73                                        + response.getStatusCode() + ", Response Text: " +
74                                        response.getStatusText());
75         }
76
77         AmazonWebServiceRequest userRequest = request.getOriginalRequest();
78         if (userRequest.getCloneRoot() != null) {
79             userRequest = userRequest.getCloneRoot();
80         }
81         responseMetadataCache.add(userRequest, awsResponse.getResponseMetadata());
82         final String awsRequestId = awsResponse.getRequestId();
83
84         if (requestLog.isDebugEnabled()) {
85             requestLog
86                     .debug("Received successful response: " + response.getStatusCode() +
87                            ", AWS Request ID: " + awsRequestId);
88         }
89
90         if (!logHeaderRequestId(response)) {
91             // Logs the AWS request ID extracted from the payload if
92             // it is not available from the response header.
93             logResponseRequestId(awsRequestId);
94         }
95
96         logExtendedRequestId(response);
97
98         awsRequestMetrics.addProperty(AWSRequestMetrics.Field.AWSRequestID, awsRequestId);
99         return fillInResponseMetadata(awsResponse, response);
100     }
101
102     @SuppressWarnings("unchecked")
103     private <T> T fillInResponseMetadata(AmazonWebServiceResponse<T> awsResponse,
104                                          HttpResponse httpResponse) {
105         final T result = awsResponse.getResult();
106         if (result instanceof AmazonWebServiceResult<?>) {
107             ((AmazonWebServiceResult) result)
108                     .setSdkResponseMetadata(awsResponse.getResponseMetadata())
109                     .setSdkHttpMetadata(SdkHttpMetadata.from(httpResponse));
110         }
111         return result;
112     }
113
114     @Override
115     public boolean needsConnectionLeftOpen() {
116         return delegate.needsConnectionLeftOpen();
117     }
118
119     /**
120      * Used to log the "x-amzn-RequestId" header at DEBUG level, if any, from the response. This
121      * method assumes the apache httpClientSettings request/response has just been successfully
122      * executed. The request id is logged using the "com.amazonaws.requestId" logger if it was
123      * enabled at DEBUG level; otherwise, it is logged at DEBUG level using the
124      * "com.amazonaws.request" logger.
125      *
126      * @return true if the AWS request id is available from the httpClientSettings header; false
127      * otherwise.
128      */

129     private boolean logHeaderRequestId(final HttpResponse response) {
130         final String reqIdHeader = response.getHeaders()
131                 .get(HttpResponseHandler.X_AMZN_REQUEST_ID_HEADER);
132         final boolean isHeaderReqIdAvail = reqIdHeader != null;
133
134         if (requestIdLog.isDebugEnabled() || requestLog.isDebugEnabled()) {
135             final String msg = HttpResponseHandler.X_AMZN_REQUEST_ID_HEADER + ": "
136                                + (isHeaderReqIdAvail ? reqIdHeader : "not available");
137             if (requestIdLog.isDebugEnabled()) {
138                 requestIdLog.debug(msg);
139             } else {
140                 requestLog.debug(msg);
141             }
142         }
143         return isHeaderReqIdAvail;
144     }
145
146     /**
147      * Used to log the request id (extracted from the response) at DEBUG level. This method is
148      * called only if there is no request id present in the httpClientSettings response header. The
149      * request id is logged using the "com.amazonaws.requestId" logger if it was enabled at DEBUG
150      * level; otherwise, it is logged using at DEBUG level using the "com.amazonaws.request"
151      * logger.
152      */

153     private void logResponseRequestId(final String awsRequestId) {
154         if (requestIdLog.isDebugEnabled() || requestLog.isDebugEnabled()) {
155             final String msg = "AWS Request ID: " +
156                                (awsRequestId == null ? "not available" : awsRequestId);
157             if (requestIdLog.isDebugEnabled()) {
158                 requestIdLog.debug(msg);
159             } else {
160                 requestLog.debug(msg);
161             }
162         }
163     }
164
165     /**
166      * Used to log the "x-amz-id-2" header at DEBUG level, if any, from the response. This
167      * method assumes the apache httpClientSettings request/response has just been successfully
168      * executed. The extended request id is logged using the "com.amazonaws.requestId" logger
169      * if it was enabled at DEBUG level; otherwise, it is logged using at DEBUG level
170      * using the "com.amazonaws.request" logger.
171      */

172     private void logExtendedRequestId(HttpResponse response) {
173         String reqId = response.getHeaders()
174                                .get(HttpResponseHandler.X_AMZN_EXTENDED_REQUEST_ID_HEADER);
175
176         if (reqId != null && (requestIdLog.isDebugEnabled() || requestLog.isDebugEnabled())) {
177             String msg = "AWS Extended Request ID: " + reqId;
178             if (requestIdLog.isDebugEnabled()) {
179                 requestIdLog.debug(msg);
180             } else {
181                 requestLog.debug(msg);
182             }
183         }
184     }
185 }
186