1 /*
2  * Copyright 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
16 package software.amazon.awssdk.services.s3.internal.handlers;
17
18 import java.util.Optional;
19 import software.amazon.awssdk.annotations.SdkInternalApi;
20 import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
21 import software.amazon.awssdk.core.SdkRequest;
22 import software.amazon.awssdk.core.interceptor.Context;
23 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
24 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
25 import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
26 import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
27 import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
28 import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
29 import software.amazon.awssdk.services.s3.model.S3Exception;
30
31 /**
32  * Translate S3Exception for the API calls that do not contain the detailed error code.
33  */

34 @SdkInternalApi
35 public final class ExceptionTranslationInterceptor implements ExecutionInterceptor {
36
37     @Override
38     public Throwable modifyException(Context.FailedExecution context, ExecutionAttributes executionAttributes) {
39
40         if (!isS3Exception404(context.exception()) || !isHeadRequest(context.request())) {
41             return context.exception();
42         }
43
44         String message = context.exception().getMessage();
45         S3Exception exception = (S3Exception) (context.exception());
46
47         String requestIdFromHeader = exception.awsErrorDetails()
48                                               .sdkHttpResponse()
49                                               .firstMatchingHeader("x-amz-request-id")
50                                               .orElse(null);
51
52         String requestId = Optional.ofNullable(exception.requestId()).orElse(requestIdFromHeader);
53
54         AwsErrorDetails errorDetails = exception.awsErrorDetails();
55
56         if (context.request() instanceof HeadObjectRequest) {
57             return NoSuchKeyException.builder()
58                                     .awsErrorDetails(fillErrorDetails(errorDetails, "NoSuchKey",
59                                                                       "The specified key does not exist."))
60                                     .statusCode(404)
61                                     .requestId(requestId)
62                                     .message(message)
63                                     .build();
64         }
65
66         if (context.request() instanceof HeadBucketRequest) {
67             return NoSuchBucketException.builder()
68                                        .awsErrorDetails(fillErrorDetails(errorDetails, "NoSuchBucket",
69                                                                          "The specified bucket does not exist."))
70                                        .statusCode(404)
71                                        .requestId(requestId)
72                                        .message(message)
73                                        .build();
74         }
75
76         return context.exception();
77     }
78
79     private AwsErrorDetails fillErrorDetails(AwsErrorDetails original, String errorCode, String errorMessage) {
80         return original.toBuilder().errorMessage(errorMessage).errorCode(errorCode).build();
81     }
82
83     private boolean isHeadRequest(SdkRequest request) {
84         return (request instanceof HeadObjectRequest || request instanceof HeadBucketRequest);
85     }
86
87     private boolean isS3Exception404(Throwable thrown) {
88         if (!(thrown instanceof S3Exception)) {
89             return false;
90         }
91
92         return ((S3Exception) thrown).statusCode() == 404;
93     }
94 }
95