diff --git a/api/src/main/java/io/minio/BucketArgs.java b/api/src/main/java/io/minio/BucketArgs.java index 63ae0a420..a7877eb10 100644 --- a/api/src/main/java/io/minio/BucketArgs.java +++ b/api/src/main/java/io/minio/BucketArgs.java @@ -16,7 +16,10 @@ package io.minio; +import io.minio.http.HttpUtils; +import io.minio.org.apache.commons.validator.routines.InetAddressValidator; import java.util.Objects; +import java.util.regex.Pattern; /** Base argument class holds bucket name and region. */ public abstract class BucketArgs extends BaseArgs { @@ -34,32 +37,35 @@ public String region() { /** Base argument builder class for {@link BucketArgs}. */ public abstract static class Builder, A extends BucketArgs> extends BaseArgs.Builder { + private static final Pattern BUCKET_NAME_REGEX = + Pattern.compile("^[a-z0-9][a-z0-9\\.\\-]{1,61}[a-z0-9]$"); + protected void validateBucketName(String name) { validateNotNull(name, "bucket name"); - // Bucket names cannot be no less than 3 and no more than 63 characters long. - if (name.length() < 3 || name.length() > 63) { + if (!BUCKET_NAME_REGEX.matcher(name).find()) { throw new IllegalArgumentException( - name + " : " + "bucket name must be at least 3 and no more than 63 characters long"); + "bucket name '" + + name + + "' does not follow Amazon S3 standards. For more information refer " + + "https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html"); } - // Successive periods in bucket names are not allowed. - if (name.contains("..")) { - String msg = - "bucket name cannot contain successive periods. For more information refer " - + "http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html"; - throw new IllegalArgumentException(name + " : " + msg); + + if (InetAddressValidator.getInstance().isValidInet4Address(name)) { + throw new IllegalArgumentException( + "bucket name '" + name + "' must not be formatted as an IP address"); } - // Bucket names should be dns compatible. - if (!name.matches("^[a-z0-9][a-z0-9\\.\\-]+[a-z0-9]$")) { - String msg = - "bucket name does not follow Amazon S3 standards. For more information refer " - + "http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html"; - throw new IllegalArgumentException(name + " : " + msg); + + if (name.contains("..") || name.contains(".-") || name.contains("-.")) { + throw new IllegalArgumentException( + "bucket name '" + name + "' cannot contain successive characters '..', '.-' and '-.'"); } } private void validateRegion(String region) { - validateNullOrNotEmptyString(region, "region"); + if (region != null && !HttpUtils.REGION_REGEX.matcher(region).find()) { + throw new IllegalArgumentException("invalid region " + region); + } } @Override diff --git a/api/src/main/java/io/minio/MinioAsyncClient.java b/api/src/main/java/io/minio/MinioAsyncClient.java index 8c028a740..405ecd589 100644 --- a/api/src/main/java/io/minio/MinioAsyncClient.java +++ b/api/src/main/java/io/minio/MinioAsyncClient.java @@ -66,6 +66,7 @@ import java.nio.file.StandardOpenOption; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; @@ -75,6 +76,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; +import java.util.regex.Matcher; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -131,22 +133,20 @@ public class MinioAsyncClient extends S3Base { private MinioAsyncClient( HttpUrl baseUrl, - String region, - boolean isAwsHost, - boolean isFipsHost, - boolean isAccelerateHost, - boolean isDualStackHost, + String awsS3Prefix, + String awsDomainSuffix, + boolean awsDualstack, boolean useVirtualStyle, + String region, Provider provider, OkHttpClient httpClient) { super( baseUrl, - region, - isAwsHost, - isFipsHost, - isAccelerateHost, - isDualStackHost, + awsS3Prefix, + awsDomainSuffix, + awsDualstack, useVirtualStyle, + region, provider, httpClient); } @@ -3213,87 +3213,66 @@ public static Builder builder() { /** Argument builder of {@link MinioClient}. */ public static final class Builder { private HttpUrl baseUrl; - private String region; - private boolean isAwsHost; - private boolean isFipsHost; - private boolean isAccelerateHost; - private boolean isDualStackHost; + private String awsS3Prefix; + private String awsDomainSuffix; + private boolean awsDualstack; private boolean useVirtualStyle; + + private String region; private Provider provider; private OkHttpClient httpClient; - private boolean isAwsChinaHost; - private String regionInUrl; + private void setAwsInfo(String host, boolean https) { + this.awsS3Prefix = null; + this.awsDomainSuffix = null; + this.awsDualstack = false; - private boolean isAwsFipsEndpoint(String endpoint) { - return endpoint.startsWith("s3-fips."); - } + if (!HttpUtils.HOSTNAME_REGEX.matcher(host).find()) return; - private boolean isAwsAccelerateEndpoint(String endpoint) { - return endpoint.startsWith("s3-accelerate."); - } + if (HttpUtils.AWS_ELB_ENDPOINT_REGEX.matcher(host).find()) { + String[] tokens = host.split("\\.elb\\.amazonaws\\.com", 1)[0].split("\\."); + this.region = tokens[tokens.length - 1]; + return; + } - private boolean isAwsEndpoint(String endpoint) { - return (endpoint.startsWith("s3.") - || isAwsFipsEndpoint(endpoint) - || isAwsAccelerateEndpoint(endpoint)) - && (endpoint.endsWith(".amazonaws.com") || endpoint.endsWith(".amazonaws.com.cn")); - } + if (!HttpUtils.AWS_ENDPOINT_REGEX.matcher(host).find()) return; - private boolean isAwsDualStackEndpoint(String endpoint) { - return endpoint.contains(".dualstack."); - } + if (!HttpUtils.AWS_S3_ENDPOINT_REGEX.matcher(host).find()) { + throw new IllegalArgumentException("invalid Amazon AWS host " + host); + } + + Matcher matcher = HttpUtils.AWS_S3_PREFIX_REGEX.matcher(host); + matcher.lookingAt(); + int end = matcher.end(); + + this.awsS3Prefix = host.substring(0, end); + if (this.awsS3Prefix.contains("s3-accesspoint") && !https) { + throw new IllegalArgumentException("use HTTPS scheme for host " + host); + } - /** - * Extracts region from AWS endpoint if available. Region is placed at second token normal - * endpoints and third token for dualstack endpoints. - * - *
Region is marked in square brackets in below examples. - *
- * https://s3.[us-east-2].amazonaws.com - * https://s3.dualstack.[ca-central-1].amazonaws.com - * https://s3.[cn-north-1].amazonaws.com.cn - * https://s3.dualstack.[cn-northwest-1].amazonaws.com.cn - */ - private String extractRegion(String endpoint) { - String[] tokens = endpoint.split("\\."); - String token = tokens[1]; - - // If token is "dualstack", then region might be in next token. - if (token.equals("dualstack")) { - token = tokens[2]; + String[] tokens = host.substring(end).split("\\."); + awsDualstack = "dualstack".equals(tokens[0]); + if (awsDualstack) tokens = Arrays.copyOfRange(tokens, 1, tokens.length); + String regionInHost = null; + if (!tokens[0].equals("vpce") && !tokens[0].equals("amazonaws")) { + regionInHost = tokens[0]; + tokens = Arrays.copyOfRange(tokens, 1, tokens.length); } + this.awsDomainSuffix = String.join(".", tokens); - // If token is equal to "amazonaws", region is not passed in the endpoint. - if (token.equals("amazonaws")) { - return null; + if (host.equals("s3-external-1.amazonaws.com")) regionInHost = "us-east-1"; + if (host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + regionInHost = "us-gov-west-1"; } - // Return token as region. - return token; + if (regionInHost != null) this.region = regionInHost; } private void setBaseUrl(HttpUrl url) { - String host = url.host(); - - this.isAwsHost = isAwsEndpoint(host); - this.isAwsChinaHost = false; - if (this.isAwsHost) { - this.isAwsChinaHost = host.endsWith(".cn"); - url = - url.newBuilder() - .host(this.isAwsChinaHost ? "amazonaws.com.cn" : "amazonaws.com") - .build(); - this.isFipsHost = isAwsFipsEndpoint(host); - this.isAccelerateHost = isAwsAccelerateEndpoint(host); - this.isDualStackHost = isAwsDualStackEndpoint(host); - this.regionInUrl = extractRegion(host); - this.useVirtualStyle = true; - } else { - this.useVirtualStyle = host.endsWith("aliyuncs.com"); - } - this.baseUrl = url; + this.setAwsInfo(url.host(), url.isHttps()); + this.useVirtualStyle = this.awsDomainSuffix != null || url.host().endsWith("aliyuncs.com"); } public Builder endpoint(String endpoint) { @@ -3325,8 +3304,9 @@ public Builder endpoint(HttpUrl url) { } public Builder region(String region) { - HttpUtils.validateNullOrNotEmptyString(region, "region"); - this.regionInUrl = region; + if (region != null && !HttpUtils.REGION_REGEX.matcher(region).find()) { + throw new IllegalArgumentException("invalid region " + region); + } this.region = region; return this; } @@ -3349,7 +3329,11 @@ public Builder httpClient(OkHttpClient httpClient) { public MinioAsyncClient build() { HttpUtils.validateNotNull(this.baseUrl, "endpoint"); - if (this.isAwsChinaHost && this.regionInUrl == null && this.region == null) { + + if (this.awsDomainSuffix != null + && this.awsDomainSuffix.endsWith(".cn") + && !this.awsS3Prefix.endsWith("s3-accelerate.") + && this.region == null) { throw new IllegalArgumentException( "Region missing in Amazon S3 China endpoint " + this.baseUrl); } @@ -3358,17 +3342,15 @@ public MinioAsyncClient build() { this.httpClient = HttpUtils.newDefaultHttpClient( DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); - if (this.region == null) this.region = regionInUrl; } return new MinioAsyncClient( baseUrl, - region, - isAwsHost, - isFipsHost, - isAccelerateHost, - isDualStackHost, + awsS3Prefix, + awsDomainSuffix, + awsDualstack, useVirtualStyle, + region, provider, httpClient); } diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index 70094ad9e..0015d1728 100644 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -2405,12 +2405,22 @@ public void traceOff() throws IOException { asyncClient.traceOff(); } - /** Enables accelerate endpoint for Amazon S3 endpoint. */ + /** + * Enables accelerate endpoint for Amazon S3 endpoint. + * + * @deprecated This method is no longer supported. + */ + @Deprecated public void enableAccelerateEndpoint() { asyncClient.enableAccelerateEndpoint(); } - /** Disables accelerate endpoint for Amazon S3 endpoint. */ + /** + * Disables accelerate endpoint for Amazon S3 endpoint. + * + * @deprecated This method is no longer supported. + */ + @Deprecated public void disableAccelerateEndpoint() { asyncClient.disableAccelerateEndpoint(); } @@ -2435,6 +2445,11 @@ public void disableVirtualStyleEndpoint() { asyncClient.disableVirtualStyleEndpoint(); } + /** Sets AWS S3 domain prefix. */ + public void setAwsS3Prefix(String awsS3Prefix) { + asyncClient.setAwsS3Prefix(awsS3Prefix); + } + public static Builder builder() { return new Builder(); } diff --git a/api/src/main/java/io/minio/S3Base.java b/api/src/main/java/io/minio/S3Base.java index aba3b0c9c..f9aca6086 100644 --- a/api/src/main/java/io/minio/S3Base.java +++ b/api/src/main/java/io/minio/S3Base.java @@ -87,6 +87,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; @@ -103,7 +104,7 @@ public abstract class S3Base { try { RequestBody.create(new byte[] {}, null); } catch (NoSuchMethodError ex) { - throw new RuntimeException("Unsupported OkHttp library found. Must use okhttp >= 4.8.1", ex); + throw new RuntimeException("Unsupported OkHttp library found. Must use okhttp >= 4.11.0", ex); } } @@ -128,16 +129,35 @@ public abstract class S3Base { private String userAgent = MinioProperties.INSTANCE.getDefaultUserAgent(); protected HttpUrl baseUrl; + protected String awsS3Prefix; + protected String awsDomainSuffix; + protected boolean awsDualstack; + protected boolean useVirtualStyle; protected String region; protected Provider provider; + protected OkHttpClient httpClient; - private boolean isAwsHost; - private boolean isFipsHost; - private boolean isAccelerateHost; - private boolean isDualStackHost; - private boolean useVirtualStyle; - private OkHttpClient httpClient; + protected S3Base( + HttpUrl baseUrl, + String awsS3Prefix, + String awsDomainSuffix, + boolean awsDualstack, + boolean useVirtualStyle, + String region, + Provider provider, + OkHttpClient httpClient) { + this.baseUrl = baseUrl; + this.awsS3Prefix = awsS3Prefix; + this.awsDomainSuffix = awsDomainSuffix; + this.awsDualstack = awsDualstack; + this.useVirtualStyle = useVirtualStyle; + this.region = region; + this.provider = provider; + this.httpClient = httpClient; + } + /** @deprecated This method is no longer supported. */ + @Deprecated protected S3Base( HttpUrl baseUrl, String region, @@ -149,24 +169,28 @@ protected S3Base( Provider provider, OkHttpClient httpClient) { this.baseUrl = baseUrl; - this.region = region; - this.isAwsHost = isAwsHost; - this.isFipsHost = isFipsHost; - this.isAccelerateHost = isAccelerateHost; - this.isDualStackHost = isDualStackHost; + if (isAwsHost) this.awsS3Prefix = "s3."; + if (isFipsHost) this.awsS3Prefix = "s3-fips."; + if (isAccelerateHost) this.awsS3Prefix = "s3-accelerate."; + if (isAwsHost || isFipsHost || isAccelerateHost) { + String host = baseUrl.host(); + if (host.endsWith(".amazonaws.com")) this.awsDomainSuffix = "amazonaws.com"; + if (host.endsWith(".amazonaws.com.cn")) this.awsDomainSuffix = "amazonaws.com.cn"; + } + this.awsDualstack = isDualStackHost; this.useVirtualStyle = useVirtualStyle; + this.region = region; this.provider = provider; this.httpClient = httpClient; } protected S3Base(S3Base client) { this.baseUrl = client.baseUrl; - this.region = client.region; - this.isAwsHost = client.isAwsHost; - this.isFipsHost = client.isFipsHost; - this.isAccelerateHost = client.isAccelerateHost; - this.isDualStackHost = client.isDualStackHost; + this.awsS3Prefix = client.awsS3Prefix; + this.awsDomainSuffix = client.awsDomainSuffix; + this.awsDualstack = client.awsDualstack; this.useVirtualStyle = client.useVirtualStyle; + this.region = client.region; this.provider = client.provider; this.httpClient = client.httpClient; } @@ -174,6 +198,18 @@ protected S3Base(S3Base client) { /** Check whether argument is valid or not. */ protected void checkArgs(BaseArgs args) { if (args == null) throw new IllegalArgumentException("null arguments"); + + if ((this.awsDomainSuffix != null) && (args instanceof BucketArgs)) { + String bucketName = ((BucketArgs) args).bucket(); + if (bucketName.startsWith("xn--") + || bucketName.endsWith("--s3alias") + || bucketName.endsWith("--ol-s3")) { + throw new IllegalArgumentException( + "bucket name '" + + bucketName + + "' must not start with 'xn--' and must not end with '--s3alias' or '--ol-s3'"); + } + } } /** Merge two Multimaps. */ @@ -275,6 +311,56 @@ private String[] handleRedirectResponse( return new String[] {code, message}; } + private String buildAwsUrl( + HttpUrl.Builder builder, String bucketName, boolean enforcePathStyle, String region) { + String host = this.awsS3Prefix + this.awsDomainSuffix; + if (host.equals("s3-external-1.amazonaws.com") + || host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + builder.host(host); + return host; + } + + host = this.awsS3Prefix; + if (this.awsS3Prefix.contains("s3-accelerate")) { + if (bucketName.contains(".")) { + throw new IllegalArgumentException( + "bucket name '" + bucketName + "' with '.' is not allowed for accelerate endpoint"); + } + if (enforcePathStyle) host = host.replaceFirst("-accelerate", ""); + } + + if (this.awsDualstack) host += "dualstack."; + if (!this.awsS3Prefix.contains("s3-accelerate")) host += region + "."; + host += this.awsDomainSuffix; + + builder.host(host); + return host; + } + + private String buildListBucketsUrl(HttpUrl.Builder builder, String region) { + if (this.awsDomainSuffix == null) return null; + + String host = this.awsS3Prefix + this.awsDomainSuffix; + if (host.equals("s3-external-1.amazonaws.com") + || host.equals("s3-us-gov-west-1.amazonaws.com") + || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { + builder.host(host); + return host; + } + + String s3Prefix = this.awsS3Prefix; + String domainSuffix = this.awsDomainSuffix; + if (this.awsS3Prefix.startsWith("s3.") || this.awsS3Prefix.startsWith("s3-")) { + s3Prefix = "s3."; + domainSuffix = "amazonaws.com" + (domainSuffix.endsWith(".cn") ? ".cn" : ""); + } + + host = s3Prefix + region + "." + domainSuffix; + builder.host(host); + return host; + } + /** Build URL for given parameters. */ protected HttpUrl buildUrl( Method method, @@ -288,72 +374,43 @@ protected HttpUrl buildUrl( } HttpUrl.Builder urlBuilder = this.baseUrl.newBuilder(); - String host = this.baseUrl.host(); - if (bucketName != null) { - boolean enforcePathStyle = false; - if (method == Method.PUT && objectName == null && queryParamMap == null) { - // use path style for make bucket to workaround "AuthorizationHeaderMalformed" error from - // s3.amazonaws.com - enforcePathStyle = true; - } else if (queryParamMap != null && queryParamMap.containsKey("location")) { - // use path style for location query - enforcePathStyle = true; - } else if (bucketName.contains(".") && this.baseUrl.isHttps()) { - // use path style where '.' in bucketName causes SSL certificate validation error - enforcePathStyle = true; - } - if (isAwsHost) { - String s3Domain = "s3."; - if (isFipsHost) { - s3Domain = "s3-fips."; - } else if (isAccelerateHost) { - if (bucketName.contains(".")) { - throw new IllegalArgumentException( - "bucket name '" - + bucketName - + "' with '.' is not allowed for accelerated endpoint"); - } - - if (!enforcePathStyle) s3Domain = "s3-accelerate."; - } + if (queryParamMap != null) { + for (Map.Entryentry : queryParamMap.entries()) { + urlBuilder.addEncodedQueryParameter( + S3Escaper.encode(entry.getKey()), S3Escaper.encode(entry.getValue())); + } + } - String dualStack = ""; - if (isDualStackHost) dualStack = "dualstack."; + if (bucketName == null) { + this.buildListBucketsUrl(urlBuilder, region); + return urlBuilder.build(); + } - String endpoint = s3Domain + dualStack; - if (enforcePathStyle || !isAccelerateHost) endpoint += region + "."; + boolean enforcePathStyle = ( + // use path style for make bucket to workaround "AuthorizationHeaderMalformed" error from + // s3.amazonaws.com + (method == Method.PUT && objectName == null && queryParamMap == null) - host = endpoint + host; - } + // use path style for location query + || (queryParamMap != null && queryParamMap.containsKey("location")) - if (enforcePathStyle || !useVirtualStyle) { - urlBuilder.host(host); - urlBuilder.addEncodedPathSegment(S3Escaper.encode(bucketName)); - } else { - urlBuilder.host(bucketName + "." + host); - } + // use path style where '.' in bucketName causes SSL certificate validation error + || (bucketName.contains(".") && this.baseUrl.isHttps())); - if (objectName != null) { - // Limitation: OkHttp does not allow to add '.' and '..' as path segment. - for (String token : objectName.split("/")) { - if (token.equals(".") || token.equals("..")) { - throw new IllegalArgumentException( - "object name with '.' or '..' path segment is not supported"); - } - } + String host = this.baseUrl.host(); + if (this.awsDomainSuffix != null) { + host = this.buildAwsUrl(urlBuilder, bucketName, enforcePathStyle, region); + } - urlBuilder.addEncodedPathSegments(S3Escaper.encodePath(objectName)); - } + if (enforcePathStyle || !this.useVirtualStyle) { + urlBuilder.addEncodedPathSegment(S3Escaper.encode(bucketName)); } else { - if (isAwsHost) urlBuilder.host("s3." + region + "." + host); + urlBuilder.host(bucketName + "." + host); } - if (queryParamMap != null) { - for (Map.Entry entry : queryParamMap.entries()) { - urlBuilder.addEncodedQueryParameter( - S3Escaper.encode(entry.getKey()), S3Escaper.encode(entry.getValue())); - } + if (objectName != null) { + urlBuilder.addEncodedPathSegments(S3Escaper.encodePath(objectName)); } return urlBuilder.build(); @@ -847,7 +904,7 @@ protected CompletableFuture getRegionAsync(String bucketName, String reg LocationConstraint lc = Xml.unmarshal(LocationConstraint.class, body.charStream()); if (lc.location() == null || lc.location().equals("")) { location = US_EAST_1; - } else if (lc.location().equals("EU")) { + } else if (lc.location().equals("EU") && this.awsDomainSuffix != null) { location = "eu-west-1"; // eu-west-1 is also referred as 'EU'. } else { location = lc.location(); @@ -1678,24 +1735,34 @@ public void traceOff() throws IOException { this.traceStream = null; } - /** Enables accelerate endpoint for Amazon S3 endpoint. */ + /** + * Enables accelerate endpoint for Amazon S3 endpoint. + * + * @deprecated This method is no longer supported. + */ + @Deprecated public void enableAccelerateEndpoint() { - this.isAccelerateHost = true; + this.awsS3Prefix = "s3-accelerate."; } - /** Disables accelerate endpoint for Amazon S3 endpoint. */ + /** + * Disables accelerate endpoint for Amazon S3 endpoint. + * + * @deprecated This method is no longer supported. + */ + @Deprecated public void disableAccelerateEndpoint() { - this.isAccelerateHost = false; + this.awsS3Prefix = "s3."; } /** Enables dual-stack endpoint for Amazon S3 endpoint. */ public void enableDualStackEndpoint() { - this.isDualStackHost = true; + this.awsDualstack = true; } /** Disables dual-stack endpoint for Amazon S3 endpoint. */ public void disableDualStackEndpoint() { - this.isDualStackHost = false; + this.awsDualstack = false; } /** Enables virtual-style endpoint. */ @@ -1708,6 +1775,15 @@ public void disableVirtualStyleEndpoint() { this.useVirtualStyle = false; } + /** Sets AWS S3 domain prefix. */ + public void setAwsS3Prefix(@Nonnull String awsS3Prefix) { + if (awsS3Prefix == null) throw new IllegalArgumentException("null Amazon AWS S3 domain prefix"); + if (!HttpUtils.AWS_S3_PREFIX_REGEX.matcher(awsS3Prefix).find()) { + throw new IllegalArgumentException("invalid Amazon AWS S3 domain prefix " + awsS3Prefix); + } + this.awsS3Prefix = awsS3Prefix; + } + /** Execute stat object asynchronously. */ protected CompletableFuture statObjectAsync(StatObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, diff --git a/api/src/main/java/io/minio/http/HttpUtils.java b/api/src/main/java/io/minio/http/HttpUtils.java index ea59e1b77..b69f3d2c2 100644 --- a/api/src/main/java/io/minio/http/HttpUtils.java +++ b/api/src/main/java/io/minio/http/HttpUtils.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -46,6 +47,36 @@ /** HTTP utilities. */ public class HttpUtils { + public static final String AWS_S3_PREFIX = + "^(((bucket\\.|accesspoint\\.)" + + "vpce(-(?!_)[a-z_\\d]+(? 253) { - throw new IllegalArgumentException("invalid hostname"); - } - - for (String label : endpoint.split("\\.")) { - if (label.length() < 1 || label.length() > 63) { - throw new IllegalArgumentException("invalid hostname"); - } - - if (!(label.matches("^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$"))) { - throw new IllegalArgumentException("invalid hostname"); - } + if (!HOSTNAME_REGEX.matcher(endpoint).find()) { + throw new IllegalArgumentException("invalid hostname " + endpoint); } }