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 modifyException(Context.FailedExecution context, ExecutionAttributes executionAttributes)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 fillErrorDetails(AwsErrorDetails original, String errorCode, String errorMessage)79 private AwsErrorDetails fillErrorDetails(AwsErrorDetails original, String errorCode, String errorMessage) { 80 return original.toBuilder().errorMessage(errorMessage).errorCode(errorCode).build(); 81 } 82 isHeadRequest(SdkRequest request)83 private boolean isHeadRequest(SdkRequest request) { 84 return (request instanceof HeadObjectRequest || request instanceof HeadBucketRequest); 85 } 86 isS3Exception404(Throwable thrown)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