/* * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazonaws.services.s3.internal; import com.amazonaws.SdkClientException; import com.amazonaws.Request; import com.amazonaws.internal.ServiceEndpointBuilder; import com.amazonaws.regions.Region; import com.amazonaws.regions.RegionUtils; import com.amazonaws.util.SdkHttpUtils; import java.net.URI; import java.net.URISyntaxException; /** * Sets endpoint and resource path on a request object */ public class S3RequestEndpointResolver { private final ServiceEndpointBuilder endpointBuilder; private final boolean isPathStyleAccess; private final String bucketName; private final String key; public S3RequestEndpointResolver(ServiceEndpointBuilder endpointBuilder, boolean isPathStyleAccess, String bucketName, String key) { this.endpointBuilder = endpointBuilder; this.isPathStyleAccess = isPathStyleAccess; this.bucketName = bucketName; this.key = key; } static boolean isValidIpV4Address(String ipAddr) { if (ipAddr == null) { return false; } String[] tokens = ipAddr.split("\\."); if (tokens.length != 4) { return false; } for (String token : tokens) { try { int tokenInt = Integer.parseInt(token); if (tokenInt < 0 || tokenInt > 255) { return false; } } catch (NumberFormatException ase) { return false; } } return true; } /** * Converts the current endpoint set for this client into virtual addressing style, by placing * the name of the specified bucket before the S3 service endpoint. * * @param bucketName The name of the bucket to use in the virtual addressing style of the returned URI. * @return A new URI, creating from the current service endpoint URI and the specified bucket. */ private static URI convertToVirtualHostEndpoint(URI endpoint, String bucketName) { try { return new URI(String.format("%s://%s.%s", endpoint.getScheme(), bucketName, endpoint.getAuthority())); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid bucket name: " + bucketName, e); } } public String getBucketName() { return this.bucketName; } /** * Set the request's endpoint and resource path with the same region it was originally * configured for * * @param request Request to set endpoint for */ public void resolveRequestEndpoint(Request<?> request) { resolveRequestEndpoint(request, null); } /** * Set the request's endpoint and resource path with the new region provided * * @param request Request to set endpoint for * @param regionString New region to determine endpoint to hit */ public void resolveRequestEndpoint(Request<?> request, String regionString) { if (regionString != null) { final Region r = RegionUtils.getRegion(regionString); if (r == null) { throw new SdkClientException("Not able to determine region" + " for " + regionString + ".Please upgrade to a newer " + "version of the SDK"); } endpointBuilder.withRegion(r); } final URI endpoint = endpointBuilder.getServiceEndpoint(); if (shouldUseVirtualAddressing(endpoint)) { request.setEndpoint(convertToVirtualHostEndpoint(endpoint, bucketName)); request.setResourcePath(SdkHttpUtils.urlEncode(getHostStyleResourcePath(), true)); } else { request.setEndpoint(endpoint); if (bucketName != null) { request.setResourcePath(SdkHttpUtils.urlEncode(getPathStyleResourcePath(), true)); } } } private boolean shouldUseVirtualAddressing(final URI endpoint) { return !isPathStyleAccess && BucketNameUtils.isDNSBucketName(bucketName) && !isValidIpV4Address(endpoint.getHost()); } private String getHostStyleResourcePath() { String resourcePath = key; /* * If the key name starts with a slash character, in order to prevent it being treated as a * path delimiter, we need to add another slash before the key name. {@see * com.amazonaws.http.HttpRequestFactory#createHttpRequest} */ if (key != null && key.startsWith("/")) { resourcePath = "/" + key; } return resourcePath; } private String getPathStyleResourcePath() { return bucketName + "/" + (key != null ? key : ""); } }