1
15 package com.amazonaws.services.s3.internal;
16
17 import com.amazonaws.AmazonServiceException;
18 import com.amazonaws.ClientConfiguration;
19 import com.amazonaws.http.HttpMethodName;
20 import com.amazonaws.http.HttpResponse;
21 import com.amazonaws.http.HttpResponseHandler;
22 import com.amazonaws.services.s3.Headers;
23 import com.amazonaws.services.s3.model.AmazonS3Exception;
24 import com.amazonaws.util.IOUtils;
25
26 import com.amazonaws.util.XmlUtils;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import java.io.ByteArrayInputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Map;
34
35 import javax.xml.stream.XMLInputFactory;
36 import javax.xml.stream.XMLStreamConstants;
37 import javax.xml.stream.XMLStreamException;
38 import javax.xml.stream.XMLStreamReader;
39
40 import static com.amazonaws.util.StringUtils.UTF8;
41
42
52 public class S3ErrorResponseHandler implements
53 HttpResponseHandler<AmazonServiceException> {
54
55 private static final Log log = LogFactory
56 .getLog(S3ErrorResponseHandler.class);
57
58 private enum S3ErrorTags {
59 Error, Message, Code, RequestId, HostId
60 };
61
62 private final ClientConfiguration clientConfiguration;
63
64 public S3ErrorResponseHandler(ClientConfiguration clientConfiguration) {
65 this.clientConfiguration = clientConfiguration;
66 }
67
68 @Override
69 public AmazonServiceException handle(HttpResponse httpResponse)
70 throws XMLStreamException {
71 final AmazonServiceException exception = createException(httpResponse);
72 exception.setHttpHeaders(httpResponse.getHeaders());
73 return exception;
74 }
75
76 private AmazonServiceException createException(HttpResponse httpResponse) throws
77 XMLStreamException {
78 final InputStream is = httpResponse.getContent();
79 String xmlContent = null;
80
85 if (is == null
86 || httpResponse.getRequest().getHttpMethod() == HttpMethodName.HEAD) {
87 return createExceptionFromHeaders(httpResponse, null);
88 }
89
90 String content = null;
91 try {
92 content = IOUtils.toString(is);
93 } catch (IOException ioe) {
94 if (log.isDebugEnabled())
95 log.debug("Failed in parsing the error response : ", ioe);
96 return createExceptionFromHeaders(httpResponse, null);
97 }
98
99 XMLStreamReader reader
100 = XmlUtils.getXmlInputFactory().createXMLStreamReader(new ByteArrayInputStream(content.getBytes(UTF8)));
101
102 try {
103
110 int targetDepth = 0;
111 final AmazonS3ExceptionBuilder exceptionBuilder = new AmazonS3ExceptionBuilder();
112 exceptionBuilder.setErrorResponseXml(content);
113 exceptionBuilder.setStatusCode(httpResponse.getStatusCode());
114 exceptionBuilder.setCloudFrontId(httpResponse.getHeaders().get(Headers.CLOUD_FRONT_ID));
115 String bucketRegion = httpResponse.getHeader(Headers.S3_BUCKET_REGION);
116 if (bucketRegion != null) {
117 exceptionBuilder.addAdditionalDetail(Headers.S3_BUCKET_REGION, bucketRegion);
118 }
119
120 boolean hasErrorTagVisited = false;
121 while (reader.hasNext()) {
122 int event = reader.next();
123
124 switch (event) {
125 case XMLStreamConstants.START_ELEMENT:
126 targetDepth++;
127 String tagName = reader.getLocalName();
128 if (targetDepth == 1
129 && !S3ErrorTags.Error.toString().equals(tagName))
130 return createExceptionFromHeaders(httpResponse,
131 "Unable to parse error response. Error XML Not in proper format."
132 + content);
133 if (S3ErrorTags.Error.toString().equals(tagName)) {
134 hasErrorTagVisited = true;
135 }
136 continue;
137 case XMLStreamConstants.CHARACTERS:
138 xmlContent = reader.getText();
139 if (xmlContent != null)
140 xmlContent = xmlContent.trim();
141 continue;
142 case XMLStreamConstants.END_ELEMENT:
143 tagName = reader.getLocalName();
144 targetDepth--;
145 if (!(hasErrorTagVisited) || targetDepth > 1) {
146 return createExceptionFromHeaders(httpResponse,
147 "Unable to parse error response. Error XML Not in proper format."
148 + content);
149 }
150 if (S3ErrorTags.Message.toString().equals(tagName)) {
151 exceptionBuilder.setErrorMessage(xmlContent);
152 } else if (S3ErrorTags.Code.toString().equals(tagName)) {
153 exceptionBuilder.setErrorCode(xmlContent);
154 } else if (S3ErrorTags.RequestId.toString().equals(tagName)) {
155 exceptionBuilder.setRequestId(xmlContent);
156 } else if (S3ErrorTags.HostId.toString().equals(tagName)) {
157 exceptionBuilder.setExtendedRequestId(xmlContent);
158 } else {
159 exceptionBuilder.addAdditionalDetail(tagName, xmlContent);
160 }
161 continue;
162 case XMLStreamConstants.END_DOCUMENT:
163 exceptionBuilder.setProxyHost(clientConfiguration.getProxyHost());
164 return exceptionBuilder.build();
165 }
166 }
167 } catch (Exception e) {
168 if (log.isDebugEnabled())
169 log.debug("Failed in parsing the error response : " + content,
170 e);
171 }
172 return createExceptionFromHeaders(httpResponse, content);
173 }
174
175 private AmazonS3Exception createExceptionFromHeaders(
176 HttpResponse errorResponse, String errorResponseXml) {
177 final Map<String, String> headers = errorResponse.getHeaders();
178 final int statusCode = errorResponse.getStatusCode();
179 final AmazonS3ExceptionBuilder exceptionBuilder = new AmazonS3ExceptionBuilder();
180 exceptionBuilder.setErrorMessage(errorResponse.getStatusText());
181 exceptionBuilder.setErrorResponseXml(errorResponseXml);
182 exceptionBuilder.setStatusCode(statusCode);
183 exceptionBuilder
184 .setExtendedRequestId(headers.get(Headers.EXTENDED_REQUEST_ID));
185 exceptionBuilder.setRequestId(headers.get(Headers.REQUEST_ID));
186 exceptionBuilder.setCloudFrontId(headers.get(Headers.CLOUD_FRONT_ID));
187 exceptionBuilder
188 .setErrorCode(statusCode + " " + errorResponse.getStatusText());
189 exceptionBuilder.addAdditionalDetail(Headers.S3_BUCKET_REGION,
190 errorResponse.getHeaders().get(Headers.S3_BUCKET_REGION));
191 exceptionBuilder.setProxyHost(clientConfiguration.getProxyHost());
192 return exceptionBuilder.build();
193 }
194
195
202 public boolean needsConnectionLeftOpen() {
203 return false;
204 }
205 }
206