diff --git a/api/src/main/java/io/minio/ErrorCode.java b/api/src/main/java/io/minio/ErrorCode.java index 00caa2142..c8153a0d6 100644 --- a/api/src/main/java/io/minio/ErrorCode.java +++ b/api/src/main/java/io/minio/ErrorCode.java @@ -25,6 +25,7 @@ public enum ErrorCode { NO_SUCH_OBJECT("NoSuchKey", "Object does not exist"), RESOURCE_NOT_FOUND("ResourceNotFound", "Request resource not found"), RESOURCE_CONFLICT("ResourceConflict", "Request resource conflicts"), + RETRY_HEAD_BUCKET("RetryHeadBucket", "Retry HEAD bucket request"), // S3 error codes ACCESS_DENIED("AccessDenied", "Access denied"), diff --git a/api/src/main/java/io/minio/HttpRequestBody.java b/api/src/main/java/io/minio/HttpRequestBody.java index d47519d79..5ef7f3423 100644 --- a/api/src/main/java/io/minio/HttpRequestBody.java +++ b/api/src/main/java/io/minio/HttpRequestBody.java @@ -17,7 +17,7 @@ package io.minio; import java.io.IOException; -import java.io.InputStream; +import java.io.BufferedInputStream; import java.io.RandomAccessFile; import java.nio.channels.Channels; @@ -30,14 +30,28 @@ * RequestBody that wraps a single data object. */ class HttpRequestBody extends RequestBody { - private final String contentType; - private final Object data; - private final int len; + private RandomAccessFile file = null; + private BufferedInputStream stream = null; + private byte[] bytes = null; + private int length = -1; + private String contentType = null; - HttpRequestBody(final String contentType, final Object data, final int len) { + HttpRequestBody(final RandomAccessFile file, final int length, final String contentType) { + this.file = file; + this.length = length; + this.contentType = contentType; + } + + HttpRequestBody(final BufferedInputStream stream, final int length, final String contentType) { + this.stream = stream; + this.length = length; + this.contentType = contentType; + } + + HttpRequestBody(final byte[] bytes, final int length, final String contentType) { + this.bytes = bytes; + this.length = length; this.contentType = contentType; - this.data = data; - this.len = len; } @Override @@ -56,30 +70,17 @@ public MediaType contentType() { @Override public long contentLength() { - if (data instanceof InputStream || data instanceof RandomAccessFile || data instanceof byte[]) { - return len; - } - - if (len == 0) { - return -1; - } else { - return len; - } + return length; } @Override public void writeTo(BufferedSink sink) throws IOException { - if (data instanceof InputStream) { - InputStream stream = (InputStream) data; - sink.writeAll(Okio.source(stream)); - } else if (data instanceof RandomAccessFile) { - RandomAccessFile file = (RandomAccessFile) data; - sink.write(Okio.source(Channels.newInputStream(file.getChannel())), len); - } else if (data instanceof byte[]) { - byte[] bytes = (byte[]) data; - sink.write(bytes, 0, len); + if (file != null) { + sink.write(Okio.source(Channels.newInputStream(file.getChannel())), length); + } else if (stream != null) { + sink.write(Okio.source(stream), length); } else { - sink.writeUtf8(data.toString()); + sink.write(bytes, 0, length); } } } diff --git a/api/src/main/java/io/minio/MinioClient.java b/api/src/main/java/io/minio/MinioClient.java index 1f0f0767f..15b2859b9 100755 --- a/api/src/main/java/io/minio/MinioClient.java +++ b/api/src/main/java/io/minio/MinioClient.java @@ -121,7 +121,6 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; @@ -792,87 +791,52 @@ private void checkObjectName(String objectName) throws InvalidArgumentException } - /** - * Sets HTTP connect, write and read timeouts. A value of 0 means no timeout, otherwise values must be between 1 and - * Integer.MAX_VALUE when converted to milliseconds. - * - *

Example:
- *
{@code minioClient.setTimeout(TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(10),
-   *                            TimeUnit.SECONDS.toMillis(30)); }
- * - * @param connectTimeout HTTP connect timeout in milliseconds. - * @param writeTimeout HTTP write timeout in milliseconds. - * @param readTimeout HTTP read timeout in milliseconds. - */ - public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) { - this.httpClient = this.httpClient.newBuilder() - .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) - .readTimeout(readTimeout, TimeUnit.MILLISECONDS) - .build(); - } - - - /** - * Ignores check on server certificate for HTTPS connection. - * - *

Example:
- *
{@code minioClient.ignoreCertCheck(); }
- * - */ - @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") - public void ignoreCertCheck() throws NoSuchAlgorithmException, KeyManagementException { - final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } + private void checkReadRequestSse(ServerSideEncryption sse) throws InvalidArgumentException { + if (sse == null) { + return; + } - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } + if (sse.type() != ServerSideEncryption.Type.SSE_C) { + throw new InvalidArgumentException("only SSE_C is supported for all read requests."); + } - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[]{}; - } - } - }; + if (sse.type().requiresTls() && !this.baseUrl.isHttps()) { + throw new InvalidArgumentException(sse.type().name() + + "operations must be performed over a secure connection."); + } + } - final SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + private void checkWriteRequestSse(ServerSideEncryption sse) throws InvalidArgumentException { + if (sse == null) { + return; + } - this.httpClient = this.httpClient.newBuilder() - .sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]) - .hostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }) - .build(); + if (sse.type().requiresTls() && !this.baseUrl.isHttps()) { + throw new InvalidArgumentException(sse.type().name() + + " operations must be performed over a secure connection."); + } } + private Map normalizeHeaders(Map headerMap) { + Map normHeaderMap = new HashMap(); + for (Map.Entry entry : headerMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + String keyLowerCased = key.toLowerCase(Locale.US); + if (amzHeaders.contains(keyLowerCased)) { + key = "x-amz-" + key; + } else if (!standardHeaders.contains(keyLowerCased) + && !keyLowerCased.startsWith("x-amz-")) { + key = "x-amz-meta-" + key; + } + normHeaderMap.put(key, value); + } + return normHeaderMap; + } - /** - * Creates Request object for given request parameters. - * - * @param method HTTP method. - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param region Amazon S3 region of the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param contentType Content type of the request body. - * @param body HTTP request body. - * @param length Length of HTTP request body. - */ - private Request createRequest(Method method, String bucketName, String objectName, - String region, Multimap headerMap, - Multimap queryParamMap, final String contentType, - Object body, int length, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InvalidKeyException, InsufficientDataException, - IOException, InternalException { + private HttpUrl buildUrl(Method method, String bucketName, String objectName, String region, + Multimap queryParamMap) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException { if (bucketName == null && objectName != null) { throw new InvalidBucketNameException(NULL_STRING, "null bucket name for object '" + objectName + "'"); } @@ -913,6 +877,12 @@ private Request createRequest(Method method, String bucketName, String objectNam 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 InvalidArgumentException("object name with '.' or '..' path segment is not supported"); + } + } + urlBuilder.addEncodedPathSegments(S3Escaper.encodePath(objectName)); } @@ -922,35 +892,55 @@ private Request createRequest(Method method, String bucketName, String objectNam } } - HttpUrl url = urlBuilder.build(); + return urlBuilder.build(); + } + private String getHostHeader(HttpUrl url) { + // ignore port when port and service matches i.e HTTP -> 80, HTTPS -> 443 + if ((url.scheme().equals("http") && url.port() == 80) + || (url.scheme().equals("https") && url.port() == 443)) { + return url.host(); + } + + return url.host() + ":" + url.port(); + } + + private Request createRequest(HttpUrl url, Method method, Multimap headerMap, Object body, + int length) + throws NoSuchAlgorithmException, InvalidArgumentException, IOException, InsufficientDataException, + InternalException { Request.Builder requestBuilder = new Request.Builder(); requestBuilder.url(url); + + String contentType = null; + String contentEncoding = null; if (headerMap != null) { + contentEncoding = headerMap.get("Content-Encoding").stream().distinct().filter(encoding -> !encoding.isEmpty()) + .collect(Collectors.joining(",")); for (Map.Entry entry : headerMap.entries()) { - requestBuilder.header(entry.getKey(), entry.getValue()); + if (entry.getKey().equals("Content-Type")) { + contentType = entry.getValue(); + } + + if (!entry.getKey().equals("Content-Encoding")) { + requestBuilder.header(entry.getKey(), entry.getValue()); + } } } + if (contentEncoding != null) { + requestBuilder.header("Content-Encoding", contentEncoding); + } + + requestBuilder.header("Host", getHostHeader(url)); + // Disable default gzip compression by okhttp library. + requestBuilder.header("Accept-Encoding", "identity"); + requestBuilder.header("User-Agent", this.userAgent); + String sha256Hash = null; String md5Hash = null; - boolean chunkedUpload = false; if (this.accessKey != null && this.secretKey != null) { - // Handle putobject specially to use chunked upload. - if (method == Method.PUT && objectName != null && body != null && body instanceof InputStream && length > 0) { - sha256Hash = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; - - String contentEncoding = "aws-chunked"; - if (headerMap != null) { - contentEncoding = Stream.concat(Stream.of("aws-chunked"), - headerMap.get("Content-Encoding").stream()).distinct().filter(encoding -> !encoding.isEmpty()) - .collect(Collectors.joining(",")); - } - requestBuilder.header("Content-Encoding", contentEncoding); - - requestBuilder.header("x-amz-decoded-content-length", Integer.toString(length)); - chunkedUpload = true; - } else if (url.isHttps()) { + if (url.isHttps()) { // Fix issue #415: No need to compute sha256 if endpoint scheme is HTTPS. sha256Hash = "UNSIGNED-PAYLOAD"; if (body != null) { @@ -964,16 +954,9 @@ private Request createRequest(Method method, String bucketName, String objectNam len = 0; } - // Disable default gzip compression by okhttp library. - requestBuilder.header("Accept-Encoding", "identity"); - if (md5Required) { - String[] hashes = Digest.sha256Md5Hashes(data, len); - sha256Hash = hashes[0]; - md5Hash = hashes[1]; - } else { - // Fix issue #567: Compute SHA256 hash only. - sha256Hash = Digest.sha256Hash(data, len); - } + String[] hashes = Digest.sha256Md5Hashes(data, len); + sha256Hash = hashes[0]; + md5Hash = hashes[1]; } } else { // Fix issue #567: Compute MD5 hash only for anonymous access. @@ -985,138 +968,46 @@ private Request createRequest(Method method, String bucketName, String objectNam if (md5Hash != null) { requestBuilder.header("Content-MD5", md5Hash); } - if (this.shouldOmitPortInHostHeader(url)) { - requestBuilder.header("Host", url.host()); - } else { - requestBuilder.header("Host", url.host() + ":" + url.port()); - } - requestBuilder.header("User-Agent", this.userAgent); + if (sha256Hash != null) { requestBuilder.header("x-amz-content-sha256", sha256Hash); } - DateTime date = new DateTime(); - requestBuilder.header("x-amz-date", date.toString(DateFormat.AMZ_DATE_FORMAT)); - - if (chunkedUpload) { - // Add empty request body for calculating seed signature. - // The actual request body is properly set below. - requestBuilder.method(method.toString(), RequestBody.create(null, "")); - Request request = requestBuilder.build(); - String seedSignature = Signer.getChunkSeedSignature(request, region, secretKey); - requestBuilder = request.newBuilder(); - ChunkedInputStream cis = new ChunkedInputStream((InputStream) body, length, date, region, this.secretKey, - seedSignature); - body = cis; - length = cis.length(); - } + DateTime dateTimeNow = new DateTime(); + requestBuilder.header("x-amz-date", dateTimeNow.toString(DateFormat.AMZ_DATE_FORMAT)); RequestBody requestBody = null; if (body != null) { - requestBody = new HttpRequestBody(contentType, body, length); + if (body instanceof RandomAccessFile) { + requestBody = new HttpRequestBody((RandomAccessFile) body, length, contentType); + } else if (body instanceof BufferedInputStream) { + requestBody = new HttpRequestBody((BufferedInputStream) body, length, contentType); + } else { + requestBody = new HttpRequestBody((byte[]) body, length, contentType); + } } requestBuilder.method(method.toString(), requestBody); - return requestBuilder.build(); - } - - - /** - * Checks whether port should be omitted in Host header. - * - *

- * HTTP Spec (rfc2616) defines that port should be omitted in Host header - * when port and service matches (i.e HTTP -> 80, HTTPS -> 443) - * - * @param url Url object - */ - private boolean shouldOmitPortInHostHeader(HttpUrl url) { - return (url.scheme().equals("http") && url.port() == 80) - || (url.scheme().equals("https") && url.port() == 443); - } - - - /** - * Executes given request parameters. - * - * @param method HTTP method. - * @param region Amazon S3 region of the bucket. - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param body HTTP request body. - * @param length Length of HTTP request body. - */ - private HttpResponse execute(Method method, String region, String bucketName, String objectName, - Map headerMap, Map queryParamMap, - Object body, int length) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - return execute(method, region, bucketName, objectName, headerMap, queryParamMap, - body, length, false); - } - - /** - * Executes given request parameters. - * - * @param method HTTP method. - * @param region Amazon S3 region of the bucket. - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param body HTTP request body. - * @param length Length of HTTP request body. - * @param md5Required Validates if md5 is required. - */ - private HttpResponse execute(Method method, String region, String bucketName, String objectName, - Map headerMap, Map queryParamMap, - Object body, int length, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - - if (headerMap != null) { - headerMap = normalizeHeaders(headerMap); - } - - Multimap queryParamMultiMap = null; - if (queryParamMap != null) { - queryParamMultiMap = Multimaps.forMap(queryParamMap); - } - - Multimap headerMultiMap = null; - if (headerMap != null) { - headerMultiMap = Multimaps.forMap(headerMap); - } - - return executeReq(method, region, bucketName, objectName, headerMultiMap, queryParamMultiMap, - body, length, md5Required); + return requestBuilder.build(); } - private HttpResponse executeReq(Method method, String region, String bucketName, String objectName, + private HttpResponse execute(Method method, String bucketName, String objectName, String region, Multimap headerMap, Multimap queryParamMap, - Object body, int length, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, XmlPullParserException, ErrorResponseException, + Object body, int length) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - String contentType = null; - if (headerMap != null && headerMap.get("Content-Type") != null) { - contentType = String.join(" ", headerMap.get("Content-Type")); - } + boolean traceRequestBody = false; if (body != null && !(body instanceof InputStream || body instanceof RandomAccessFile || body instanceof byte[])) { byte[] bytes = body.toString().getBytes(StandardCharsets.UTF_8); body = bytes; length = bytes.length; + traceRequestBody = true; } - Request request = createRequest(method, bucketName, objectName, region, - headerMap, queryParamMap, - contentType, body, length, md5Required); - + HttpUrl url = buildUrl(method, bucketName, objectName, region, queryParamMap); + Request request = createRequest(url, method, headerMap, body, length); if (this.accessKey != null && this.secretKey != null) { request = Signer.signV4(request, region, accessKey, secretKey); } @@ -1133,6 +1024,10 @@ private HttpResponse executeReq(Method method, String region, String bucketName, .replaceAll("Signature=([0-9a-f]+)", "Signature=*REDACTED*") .replaceAll("Credential=([^/]+)", "Credential=*REDACTED*"); this.traceStream.println(headers); + if (traceRequestBody) { + this.traceStream.println(); + this.traceStream.println(new String((byte[]) body, StandardCharsets.UTF_8)); + } } Response response = this.httpClient.newCall(request).execute(); @@ -1151,36 +1046,29 @@ private HttpResponse executeReq(Method method, String region, String bucketName, return new HttpResponse(header, response); } - ErrorResponse errorResponse = null; - - // HEAD returns no body, and fails on parseXml - if (!method.equals(Method.HEAD)) { - Scanner scanner = new Scanner(response.body().charStream()); - try { - scanner.useDelimiter("\\A"); - String errorXml = ""; + // Error in case of Non-XML response from server for non-HEAD requests. + if (!"application/xml".equals(response.headers().get("content-type")) && !method.equals(Method.HEAD)) { + throw new InvalidResponseException(); + } - // read entire body stream to string. - if (scanner.hasNext()) { - errorXml = scanner.next(); - } - - // Error in case of Non-XML response from server - if (!("application/xml".equals(response.headers().get("content-type")))) { - throw new InvalidResponseException(); - } - errorResponse = new ErrorResponse(new StringReader(errorXml)); - if (this.traceStream != null) { - this.traceStream.println(errorXml); - } + String errorXml = null; + try { + errorXml = new String(response.body().bytes(), StandardCharsets.UTF_8); + } finally { + response.close(); + } - } finally { - response.body().close(); - scanner.close(); - } + ErrorResponse errorResponse = null; + if (!"".equals(errorXml)) { + errorResponse = new ErrorResponse(new StringReader(errorXml)); + } else if (!method.equals(Method.HEAD)) { + throw new InvalidResponseException(); } if (this.traceStream != null) { + if (!("".equals(errorXml) && method.equals(Method.HEAD))) { + this.traceStream.println(errorXml); + } this.traceStream.println(END_HTTP); } @@ -1191,7 +1079,13 @@ private HttpResponse executeReq(Method method, String region, String bucketName, ec = ErrorCode.REDIRECT; break; case 400: - ec = ErrorCode.INVALID_URI; + // HEAD bucket with wrong region gives 400 without body. + if (method.equals(Method.HEAD) && bucketName != null && objectName == null + && BucketRegionCache.INSTANCE.exists(bucketName)) { + ec = ErrorCode.RETRY_HEAD_BUCKET; + } else { + ec = ErrorCode.INVALID_URI; + } break; case 404: if (objectName != null) { @@ -1226,29 +1120,59 @@ private HttpResponse executeReq(Method method, String region, String bucketName, } // invalidate region cache if needed - if (errorResponse.errorCode() == ErrorCode.NO_SUCH_BUCKET) { + if (errorResponse.errorCode() == ErrorCode.NO_SUCH_BUCKET + || errorResponse.errorCode() == ErrorCode.RETRY_HEAD_BUCKET) { BucketRegionCache.INSTANCE.remove(bucketName); // TODO: handle for other cases as well - // observation: on HEAD of a bucket with wrong region gives 400 without body } throw new ErrorResponseException(errorResponse, response); } + private HttpResponse execute(Method method, String bucketName, String objectName, String region, + Map headerMap, Map queryParamMap, + Object body, int length) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException { + Multimap headerMultiMap = null; + if (headerMap != null) { + headerMultiMap = Multimaps.forMap(normalizeHeaders(headerMap)); + } + + Multimap queryParamMultiMap = null; + if (queryParamMap != null) { + queryParamMultiMap = Multimaps.forMap(queryParamMap); + } + + return execute(method, bucketName, objectName, region, headerMultiMap, queryParamMultiMap, body, length); + } + + /** + * Returns text of given XML element. + * + * @throws XmlPullParserException upon parsing response xml + */ + private String getText(XmlPullParser xpp) throws XmlPullParserException { + if (xpp.getEventType() == XmlPullParser.TEXT) { + return xpp.getText(); + } + return null; + } + /** * Updates Region cache for given bucket. */ private void updateRegionCache(String bucketName) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { if (bucketName != null && this.accessKey != null && this.secretKey != null && !BucketRegionCache.INSTANCE.exists(bucketName)) { Map queryParamMap = new HashMap<>(); queryParamMap.put("location", null); - HttpResponse response = execute(Method.GET, US_EAST_1, bucketName, null, - null, queryParamMap, null, 0); + HttpResponse response = execute(Method.GET, bucketName, null, US_EAST_1, null, queryParamMap, null, 0); // existing XmlEntity does not work, so fallback to regular parsing. XmlPullParser xpp = xmlPullParserFactory.newPullParser(); @@ -1287,9 +1211,10 @@ private void updateRegionCache(String bucketName) * Computes region of a given bucket name. If set, this.region is considered. Otherwise, * resort to the server location API. */ - private String getRegion(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, - ErrorResponseException, InternalException, InvalidResponseException { + private String getRegion(String bucketName) + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, InvalidArgumentException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, + InvalidResponseException { String region; if (this.region == null || "".equals(this.region)) { updateRegionCache(bucketName); @@ -1300,271 +1225,91 @@ private String getRegion(String bucketName) throws InvalidBucketNameException, N return region; } - /** - * Returns text of given XML element. - * - * @throws XmlPullParserException upon parsing response xml - */ - private String getText(XmlPullParser xpp) throws XmlPullParserException { - if (xpp.getEventType() == XmlPullParser.TEXT) { - return xpp.getText(); - } - return null; - } - - private void checkReadRequestSse(ServerSideEncryption sse) throws InvalidArgumentException { - if (sse == null) { - return; - } - - if (sse.type() != ServerSideEncryption.Type.SSE_C) { - throw new InvalidArgumentException("only SSE_C is supported for all read requests."); - } - - if (sse.type().requiresTls() && !this.baseUrl.isHttps()) { - throw new InvalidArgumentException(sse.type().name() - + "operations must be performed over a secure connection."); - } - } - - private void checkWriteRequestSse(ServerSideEncryption sse) throws InvalidArgumentException { - if (sse == null) { - return; - } - - if (sse.type().requiresTls() && !this.baseUrl.isHttps()) { - throw new InvalidArgumentException(sse.type().name() - + " operations must be performed over a secure connection."); - } - } - - /** - * Executes GET method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - */ private HttpResponse executeGet(String bucketName, String objectName, Map headerMap, Map queryParamMap) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - return execute(Method.GET, getRegion(bucketName), bucketName, objectName, headerMap, queryParamMap, null, 0); - } - - - /** - * Executes HEAD method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - */ - private HttpResponse executeHead(String bucketName, String objectName) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - HttpResponse response = execute(Method.HEAD, getRegion(bucketName), bucketName, objectName, null, - null, null, 0); - response.body().close(); - return response; - } - - /** - * Executes HEAD method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of header parameters of the request. - */ - private HttpResponse executeHead(String bucketName, String objectName, Map headerMap) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - - HttpResponse response = execute(Method.HEAD, getRegion(bucketName), bucketName, objectName, headerMap, - null, null, 0); - response.body().close(); - return response; - } - - /** - * Executes DELETE method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param queryParamMap Map of HTTP query parameters of the request. - */ - private HttpResponse executeDelete(String bucketName, String objectName, Map queryParamMap) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - HttpResponse response = execute(Method.DELETE, getRegion(bucketName), bucketName, objectName, null, - queryParamMap, null, 0); - response.body().close(); - return response; - } - - - /** - * Executes POST method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param data HTTP request body data. - */ - private HttpResponse executePost(String bucketName, String objectName, Map headerMap, - Map queryParamMap, Object data) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - return execute(Method.POST, getRegion(bucketName), bucketName, objectName, headerMap, - queryParamMap, data, 0); + return execute(Method.GET, bucketName, objectName, getRegion(bucketName), headerMap, queryParamMap, null, 0); } - /** - * Executes POST method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param data HTTP request body data. - * @param md5Required Is MD5 calculations required. - */ - private HttpResponse executePost(String bucketName, String objectName, Map headerMap, - Map queryParamMap, Object data, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + private HttpResponse executeGet(String bucketName, String objectName, Multimap headerMap, + Multimap queryParamMap) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - return execute(Method.POST, getRegion(bucketName), bucketName, objectName, headerMap, - queryParamMap, data, 0, md5Required); + return execute(Method.GET, bucketName, objectName, getRegion(bucketName), null, queryParamMap, null, 0); } + private HttpResponse executeHead(String bucketName, String objectName, Map headerMap, + Map queryParamMap) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException { + HttpResponse response = execute(Method.HEAD, bucketName, objectName, getRegion(bucketName), + headerMap, queryParamMap, null, 0); + response.body().close(); + return response; + } - private Map normalizeHeaders(Map headerMap) { - Map normHeaderMap = new HashMap(); - for (Map.Entry entry : headerMap.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - String keyLowerCased = key.toLowerCase(Locale.US); - if (amzHeaders.contains(keyLowerCased)) { - key = "x-amz-" + key; - } else if (!standardHeaders.contains(keyLowerCased) - && !keyLowerCased.startsWith("x-amz-")) { - key = "x-amz-meta-" + key; + private HttpResponse executeHead(String bucketName, String objectName) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException { + try { + return executeHead(bucketName, objectName, null, null); + } catch (ErrorResponseException e) { + if (e.errorResponse().errorCode() != ErrorCode.RETRY_HEAD_BUCKET) { + throw e; } - normHeaderMap.put(key, value); } - return normHeaderMap; + + // Retry once for RETRY_HEAD_BUCKET error. + return executeHead(bucketName, objectName, null, null); } - /** - * Executes PUT method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param region Amazon S3 region of the bucket. - * @param data HTTP request body data. - * @param length Length of HTTP request body data. - */ - private HttpResponse executePut(String bucketName, String objectName, Map headerMap, - Map queryParamMap, String region, Object data, int length) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + private HttpResponse executeHead(String bucketName, String objectName, Map headerMap) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - HttpResponse response = execute(Method.PUT, region, bucketName, objectName, - headerMap, queryParamMap, - data, length); - return response; + return executeHead(bucketName, objectName, headerMap, null); } - /** - * Executes PUT method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param region Amazon S3 region of the bucket. - * @param data HTTP request body data. - * @param length Length of HTTP request body data. - * @param md5Required Is MD5 calculations required. - */ - private HttpResponse executePut(String bucketName, String objectName, Map headerMap, - Map queryParamMap, String region, Object data, int length, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + private HttpResponse executeDelete(String bucketName, String objectName, Map queryParamMap) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - HttpResponse response = execute(Method.PUT, region, bucketName, objectName, - headerMap, queryParamMap, - data, length, md5Required); + HttpResponse response = execute(Method.DELETE, bucketName, objectName, getRegion(bucketName), null, + queryParamMap, null, 0); + response.body().close(); return response; } + private HttpResponse executePost(String bucketName, String objectName, Map headerMap, + Map queryParamMap, Object data) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException { + return execute(Method.POST, bucketName, objectName, getRegion(bucketName), headerMap, + queryParamMap, data, 0); + } - /** - * Executes PUT method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param data HTTP request body data. - * @param length Length of HTTP request body data. - */ - private HttpResponse executePut(String bucketName, String objectName, Map headerMap, + private HttpResponse executePut(String bucketName, String objectName, String region, Map headerMap, Map queryParamMap, Object data, int length) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - return executePut(bucketName, objectName, headerMap, queryParamMap, getRegion(bucketName), data, length, false); + return execute(Method.PUT, bucketName, objectName, region, headerMap, queryParamMap, data, length); } - /** - * Executes PUT method for given request parameters. - * - * @param bucketName Bucket name. - * @param objectName Object name in the bucket. - * @param headerMap Map of HTTP headers for the request. - * @param queryParamMap Map of HTTP query parameters of the request. - * @param data HTTP request body data. - * @param length Length of HTTP request body data. - * @param md5Required Is MD5 calculations required. - */ private HttpResponse executePut(String bucketName, String objectName, Map headerMap, - Map queryParamMap, Object data, int length, boolean md5Required) - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + Map queryParamMap, Object data, int length) + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, + IOException, InvalidKeyException, XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { - return executePut(bucketName, objectName, headerMap, queryParamMap, - getRegion(bucketName), data, length, md5Required); - } - - - /** - * Sets application's name/version to user agent. For more information about user agent - * refer #rfc2616. - * - * @param name Your application name. - * @param version Your application version. - */ - @SuppressWarnings("unused") - public void setAppInfo(String name, String version) { - if (name == null || version == null) { - // nothing to do - return; - } - - this.userAgent = DEFAULT_USER_AGENT + " " + name.trim() + "/" + version.trim(); + return executePut(bucketName, objectName, getRegion(bucketName), headerMap, queryParamMap, data, length); } - /** * Returns meta data information of given object in given bucket. * @@ -1678,10 +1423,8 @@ public ObjectStat statObject(String bucketName, String objectName, ServerSideEnc public String getObjectUrl(String bucketName, String objectName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - Request request = createRequest(Method.GET, bucketName, objectName, getRegion(bucketName), - null, null, null, null, 0, false); - HttpUrl url = request.url(); + InternalException, InvalidResponseException, InvalidArgumentException { + HttpUrl url = buildUrl(Method.GET, bucketName, objectName, getRegion(bucketName), null); return url.toString(); } @@ -2691,7 +2434,7 @@ private String uploadPartCopy(String bucketName, String objectName, String uploa Map headerMap) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("partNumber", Integer.toString(partNumber)); queryParamMap.put("uploadId", uploadId); @@ -2741,7 +2484,7 @@ public String getPresignedObjectUrl(Method method, String bucketName, String obj Map reqParams) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { // Validate input. if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) { throw new InvalidExpiresRangeException(expires, "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME); @@ -2761,8 +2504,9 @@ public String getPresignedObjectUrl(Method method, String bucketName, String obj } String region = getRegion(bucketName); - Request request = createRequest(method, bucketName, objectName, region, null, queryParamMap, null, body, 0, false); - HttpUrl url = Signer.presignV4(request, region, accessKey, secretKey, expires); + HttpUrl url = buildUrl(method, bucketName, objectName, region, queryParamMap); + Request request = createRequest(url, method, null, body, 0); + url = Signer.presignV4(request, region, accessKey, secretKey, expires); return url.toString(); } @@ -2800,7 +2544,7 @@ public String presignedGetObject(String bucketName, String objectName, Integer e Map reqParams) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { return getPresignedObjectUrl(Method.GET, bucketName, objectName, expires, reqParams); } @@ -2835,7 +2579,7 @@ public String presignedGetObject(String bucketName, String objectName, Integer e public String presignedGetObject(String bucketName, String objectName, Integer expires) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { return presignedGetObject(bucketName, objectName, expires, null); } @@ -2871,7 +2615,7 @@ public String presignedGetObject(String bucketName, String objectName, Integer e public String presignedGetObject(String bucketName, String objectName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { return presignedGetObject(bucketName, objectName, DEFAULT_EXPIRY_TIME, null); } @@ -2907,7 +2651,7 @@ public String presignedGetObject(String bucketName, String objectName) public String presignedPutObject(String bucketName, String objectName, Integer expires) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { return getPresignedObjectUrl(Method.PUT, bucketName, objectName, expires, null); } @@ -2943,7 +2687,7 @@ public String presignedPutObject(String bucketName, String objectName, Integer e public String presignedPutObject(String bucketName, String objectName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidExpiresRangeException, InvalidResponseException { + InternalException, InvalidExpiresRangeException, InvalidResponseException, InvalidArgumentException { return presignedPutObject(bucketName, objectName, DEFAULT_EXPIRY_TIME); } @@ -3032,24 +2776,18 @@ public void removeObject(String bucketName, String objectName) private List removeObject(String bucketName, List objectList) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("delete", ""); DeleteRequest request = new DeleteRequest(objectList); - HttpResponse response = executePost(bucketName, null, null, queryParamMap, request, true); + HttpResponse response = executePost(bucketName, null, null, queryParamMap, request); String bodyContent = ""; - // Use scanner to read entire body stream to string. - Scanner scanner = new Scanner(response.body().charStream()); try { - scanner.useDelimiter("\\A"); - if (scanner.hasNext()) { - bodyContent = scanner.next(); - } + bodyContent = new String(response.body().bytes(), StandardCharsets.UTF_8); } finally { response.body().close(); - scanner.close(); } List errorList = null; @@ -3116,7 +2854,7 @@ private synchronized void populate() { } } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException | InvalidResponseException e) { + | InternalException | InvalidResponseException | InvalidArgumentException e) { this.error = new Result<>(null, e); } finally { if (errorList != null) { @@ -3312,7 +3050,7 @@ private synchronized void populate() { this.listBucketResult = listObjectsV2(bucketName, continuationToken, prefix, delimiter); } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException | InvalidResponseException e) { + | InternalException | InvalidResponseException | InvalidArgumentException e) { this.error = new Result<>(null, e); } finally { if (this.listBucketResult != null) { @@ -3419,7 +3157,7 @@ public void remove() { private ListBucketResult listObjectsV2(String bucketName, String continuationToken, String prefix, String delimiter) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("list-type", "2"); @@ -3483,7 +3221,7 @@ private synchronized void populate() { this.listBucketResult = listObjectsV1(bucketName, marker, prefix, delimiter); } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException | InvalidResponseException e) { + | InternalException | InvalidResponseException | InvalidArgumentException e) { this.error = new Result<>(null, e); } finally { if (this.listBucketResult != null) { @@ -3591,7 +3329,7 @@ public void remove() { private ListBucketResultV1 listObjectsV1(String bucketName, String marker, String prefix, String delimiter) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); if (marker != null) { @@ -3646,8 +3384,8 @@ private ListBucketResultV1 listObjectsV1(String bucketName, String marker, Strin public List listBuckets() throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { - HttpResponse response = executeGet(null, null, null, null); + InternalException, InvalidResponseException, InvalidArgumentException { + HttpResponse response = executeGet(null, null, null, (Multimap) null); ListAllMyBucketsResult result = new ListAllMyBucketsResult(); result.parseXml(response.body().charStream()); response.body().close(); @@ -3687,7 +3425,7 @@ public List listBuckets() public boolean bucketExists(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { try { executeHead(bucketName, null); return true; @@ -3725,7 +3463,7 @@ public boolean bucketExists(String bucketName) public void makeBucket(String bucketName) throws InvalidBucketNameException, RegionConflictException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { this.makeBucket(bucketName, null, false); } @@ -3759,7 +3497,7 @@ public void makeBucket(String bucketName) public void makeBucket(String bucketName, String region) throws InvalidBucketNameException, RegionConflictException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { this.makeBucket(bucketName, region, false); } @@ -3794,7 +3532,7 @@ public void makeBucket(String bucketName, String region) public void makeBucket(String bucketName, String region, boolean objectLock) throws InvalidBucketNameException, RegionConflictException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { // If region param is not provided, set it with the one provided by constructor if (region == null) { region = this.region; @@ -3820,7 +3558,7 @@ public void makeBucket(String bucketName, String region, boolean objectLock) headerMap.put("x-amz-bucket-object-lock-enabled", "true"); } - HttpResponse response = executePut(bucketName, null, headerMap, null, region, configString, 0); + HttpResponse response = executePut(bucketName, null, region, headerMap, null, configString, 0); response.body().close(); } @@ -3851,7 +3589,7 @@ public void makeBucket(String bucketName, String region, boolean objectLock) public void enableVersioning(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("versioning", ""); String config = "" @@ -3887,7 +3625,7 @@ public void enableVersioning(String bucketName) public void disableVersioning(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("versioning", ""); String config = "" @@ -3924,7 +3662,7 @@ public void disableVersioning(String bucketName) public void setDefaultRetention(String bucketName, ObjectLockConfiguration config) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("object-lock", ""); @@ -3958,7 +3696,7 @@ public void setDefaultRetention(String bucketName, ObjectLockConfiguration confi public ObjectLockConfiguration getDefaultRetention(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("object-lock", ""); @@ -4061,7 +3799,7 @@ public void setObjectRetention(String bucketName, String objectName, String vers public ObjectRetentionConfiguration getObjectRetention(String bucketName, String objectName, String versionId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("retention", ""); @@ -4110,7 +3848,7 @@ public ObjectRetentionConfiguration getObjectRetention(String bucketName, String public void enableObjectLegalHold(String bucketName, String objectName, String versionId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("legal-hold", ""); @@ -4155,7 +3893,7 @@ public void enableObjectLegalHold(String bucketName, String objectName, String v public void disableObjectLegalHold(String bucketName, String objectName, String versionId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("legal-hold", ""); @@ -4202,7 +3940,7 @@ public void disableObjectLegalHold(String bucketName, String objectName, String public boolean isObjectLegalHoldEnabled(String bucketName, String objectName, String versionId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("legal-hold", ""); @@ -4254,7 +3992,7 @@ public boolean isObjectLegalHoldEnabled(String bucketName, String objectName, St public void removeBucket(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { executeDelete(bucketName, null, null); } @@ -4379,7 +4117,7 @@ public void putObject(String bucketName, String objectName, String fileName, Str public void putObject(String bucketName, String objectName, String fileName, ServerSideEncryption sse) throws InvalidBucketNameException, NoSuchAlgorithmException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidArgumentException, InsufficientDataException,InvalidResponseException { + InternalException, InvalidArgumentException, InsufficientDataException, InvalidResponseException { putObject(bucketName, objectName, fileName, null, null, sse, null); } @@ -4423,9 +4161,9 @@ public void putObject(String bucketName, String objectName, String fileName, Ser @Deprecated public void putObject(String bucketName, String objectName, String fileName, Long size, Map headerMap, ServerSideEncryption sse, String contentType) - throws InvalidBucketNameException, NoSuchAlgorithmException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidArgumentException, InsufficientDataException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, IOException, InvalidKeyException, NoResponseException, + XmlPullParserException, ErrorResponseException, InternalException, InvalidArgumentException, + InsufficientDataException, InvalidResponseException { if (fileName == null || "".equals(fileName)) { throw new InvalidArgumentException("empty file name is not allowed"); } @@ -5176,7 +4914,7 @@ private String putObject(String bucketName, String objectName, Object data, int Map headerMap, String uploadId, int partNumber) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { HttpResponse response = null; Map queryParamMap = null; @@ -5415,7 +5153,7 @@ public String getBucketPolicy(String bucketName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, InternalException, BucketPolicyTooLargeException, - InvalidResponseException { + InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("policy", ""); @@ -5503,9 +5241,9 @@ public String getBucketPolicy(String bucketName) * @throws InvalidResponseException upon a non-xml response from server */ public void setBucketPolicy(String bucketName, String policy) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Map headerMap = new HashMap<>(); headerMap.put("Content-Type", "application/json"); @@ -5542,18 +5280,16 @@ public void setBucketPolicy(String bucketName, String policy) * @throws InvalidResponseException upon a non-xml response from server */ public void setBucketLifeCycle(String bucketName, String lifeCycle) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException,InvalidArgumentException, - InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidArgumentException, InvalidResponseException { if ((lifeCycle == null) || "".equals(lifeCycle)) { throw new InvalidArgumentException("life cycle cannot be empty"); } - Map headerMap = new HashMap<>(); - headerMap.put("Content-Length", Integer.toString(lifeCycle.length())); + Map queryParamMap = new HashMap<>(); queryParamMap.put("lifecycle", ""); - HttpResponse response = executePut(bucketName, null, headerMap, queryParamMap, lifeCycle, 0, true); + HttpResponse response = executePut(bucketName, null, null, queryParamMap, lifeCycle, 0); response.body().close(); } @@ -5578,12 +5314,12 @@ public void setBucketLifeCycle(String bucketName, String lifeCycle) * @throws InvalidResponseException upon a non-xml response from server */ public void deleteBucketLifeCycle(String bucketName) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("lifecycle", ""); - HttpResponse response = executeDelete(bucketName, "", queryParamMap); + HttpResponse response = executeDelete(bucketName, "", queryParamMap); response.body().close(); } @@ -5609,34 +5345,26 @@ public void deleteBucketLifeCycle(String bucketName) * */ public String getBucketLifeCycle(String bucketName) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("lifecycle", ""); - HttpResponse response = null; String bodyContent = ""; - Scanner scanner = null ; + HttpResponse response = null; try { - response = executeGet(bucketName, "", null, queryParamMap); - scanner = new Scanner(response.body().charStream()); - // read entire body stream to string. - scanner.useDelimiter("\\A"); - if (scanner.hasNext()) { - bodyContent = scanner.next(); - } + response = executeGet(bucketName, null, null, queryParamMap); + bodyContent = new String(response.body().bytes(), StandardCharsets.UTF_8); } catch (ErrorResponseException e) { if (e.errorResponse().errorCode() != ErrorCode.NO_SUCH_LIFECYCLE_CONFIGURATION) { throw e; } } finally { - if (response != null && response.body() != null) { + if (response != null) { response.body().close(); } - if (scanner != null) { - scanner.close(); - } } + return bodyContent; } @@ -5665,9 +5393,9 @@ public String getBucketLifeCycle(String bucketName) * */ public NotificationConfiguration getBucketNotification(String bucketName) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("notification", ""); @@ -5709,9 +5437,9 @@ public NotificationConfiguration getBucketNotification(String bucketName) * */ public void setBucketNotification(String bucketName, NotificationConfiguration notificationConfiguration) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put("notification", ""); HttpResponse response = executePut(bucketName, null, null, queryParamMap, notificationConfiguration.toString(), 0); @@ -5743,9 +5471,9 @@ public void setBucketNotification(String bucketName, NotificationConfiguration n * @throws InvalidResponseException upon a non-xml response from server */ public void removeAllBucketNotification(String bucketName) - throws InvalidBucketNameException, NoSuchAlgorithmException, - InsufficientDataException, IOException, InvalidKeyException, NoResponseException, - XmlPullParserException, ErrorResponseException, InternalException, InvalidResponseException { + throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { NotificationConfiguration notificationConfiguration = new NotificationConfiguration(); setBucketNotification(bucketName, notificationConfiguration); } @@ -5834,7 +5562,7 @@ private synchronized void populate() { prefix, delimiter, 1000); } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException | InvalidResponseException e) { + | InternalException | InvalidResponseException | InvalidArgumentException e) { this.error = new Result<>(null, e); } finally { if (this.listMultipartUploadsResult != null) { @@ -5848,7 +5576,7 @@ private synchronized void populate() { private synchronized long getAggregatedPartSize(String objectName, String uploadId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException { + InternalException, InvalidArgumentException { long aggregatedPartSize = 0; for (Result result : listObjectParts(bucketName, objectName, uploadId)) { @@ -5919,7 +5647,7 @@ public Result next() { aggregatedPartSize = getAggregatedPartSize(upload.objectName(), upload.uploadId()); } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException e) { + | InternalException | InvalidArgumentException e) { // special case: ignore the error as we can't propagate the exception in next() aggregatedPartSize = -1; } @@ -5951,7 +5679,7 @@ private ListMultipartUploadsResult listIncompleteUploads(String bucketName, Stri String prefix, String delimiter, int maxUploads) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { if (maxUploads < 0 || maxUploads > 1000) { maxUploads = 1000; } @@ -5995,7 +5723,7 @@ private ListMultipartUploadsResult listIncompleteUploads(String bucketName, Stri private String initMultipartUpload(String bucketName, String objectName, Map headerMap) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException , InvalidResponseException { + InternalException , InvalidResponseException, InvalidArgumentException { // set content type if not set already if ((headerMap != null) && (headerMap.get("Content-Type") == null)) { headerMap.put("Content-Type", "application/octet-stream"); @@ -6019,29 +5747,20 @@ private String initMultipartUpload(String bucketName, String objectName, Map queryParamMap = new HashMap<>(); queryParamMap.put(UPLOAD_ID, uploadId); - CompleteMultipartUpload completeManifest = new CompleteMultipartUpload(parts); - HttpResponse response = executePost(bucketName, objectName, null, queryParamMap, completeManifest); - - // Fixing issue https://github.com/minio/minio-java/issues/391 String bodyContent = ""; - Scanner scanner = new Scanner(response.body().charStream()); try { - // read entire body stream to string. - scanner.useDelimiter("\\A"); - if (scanner.hasNext()) { - bodyContent = scanner.next(); - } + bodyContent = new String(response.body().bytes(), StandardCharsets.UTF_8); + bodyContent = bodyContent.trim(); } finally { response.body().close(); - scanner.close(); } - bodyContent = bodyContent.trim(); + // Handle if body contains error. if (!bodyContent.isEmpty()) { ErrorResponse errorResponse = new ErrorResponse(new StringReader(bodyContent)); if (errorResponse.code() != null) { @@ -6075,7 +5794,7 @@ private synchronized void populate() { this.listPartsResult = listObjectParts(bucketName, objectName, uploadId, nextPartNumberMarker); } catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException - | InternalException | InvalidResponseException e) { + | InternalException | InvalidResponseException | InvalidArgumentException e) { this.error = new Result<>(null, e); } finally { if (this.listPartsResult != null) { @@ -6158,7 +5877,7 @@ public void remove() { private ListPartsResult listObjectParts(String bucketName, String objectName, String uploadId, int partNumberMarker) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put(UPLOAD_ID, uploadId); if (partNumberMarker > 0) { @@ -6180,7 +5899,7 @@ private ListPartsResult listObjectParts(String bucketName, String objectName, St private void abortMultipartUpload(String bucketName, String objectName, String uploadId) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { Map queryParamMap = new HashMap<>(); queryParamMap.put(UPLOAD_ID, uploadId); executeDelete(bucketName, objectName, queryParamMap); @@ -6214,7 +5933,7 @@ private void abortMultipartUpload(String bucketName, String objectName, String u public void removeIncompleteUpload(String bucketName, String objectName) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InternalException, InvalidResponseException, InvalidArgumentException { for (Result r : listIncompleteUploads(bucketName, objectName, true, false)) { Upload upload = r.get(); if (objectName.equals(upload.objectName())) { @@ -6251,8 +5970,8 @@ public void removeIncompleteUpload(String bucketName, String objectName) public void listenBucketNotification(String bucketName, String prefix, String suffix, String[] events, BucketEventListener eventCallback) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, - InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, - InternalException, InvalidResponseException { + InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, + InternalException, InvalidResponseException, InvalidArgumentException { Multimap queryParamMap = HashMultimap.create(); queryParamMap.put("prefix", prefix); @@ -6267,8 +5986,7 @@ public void listenBucketNotification(String bucketName, String prefix, String su ObjectMapper mapper = new ObjectMapper(); try { - response = executeReq(Method.GET, getRegion(bucketName), - bucketName, "", null, queryParamMap, null, 0, false); + response = executeGet(bucketName, "", null, queryParamMap); scanner = new Scanner(response.body().charStream()); scanner.useDelimiter("\n"); while (scanner.hasNext()) { @@ -6323,8 +6041,8 @@ public void listenBucketNotification(String bucketName, String prefix, String su public CloseableIterator> listenBucketNotification(String bucketName, String prefix, String suffix, String[] events) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, - InvalidResponseException, InternalException, NoResponseException, InvalidBucketNameException, - XmlPullParserException, ErrorResponseException { + InvalidResponseException, InternalException, NoResponseException, InvalidBucketNameException, + XmlPullParserException, ErrorResponseException, InvalidArgumentException { Multimap queryParamMap = HashMultimap.create(); queryParamMap.put("prefix", prefix); @@ -6333,8 +6051,7 @@ public CloseableIterator> listenBucketNotification(Stri queryParamMap.put("events", event); } - HttpResponse response = executeReq(Method.GET, getRegion(bucketName), - bucketName, "", null, queryParamMap, null, 0, false); + HttpResponse response = executeGet(bucketName, "", null, queryParamMap); return new CloseableIterator>() { Scanner scanner = new Scanner(response.body().charStream()).useDelimiter("\n"); @@ -6468,8 +6185,7 @@ public SelectResponseStream selectObjectContent(String bucketName, String object SelectObjectContentRequest request = new SelectObjectContentRequest(sqlExpression, requestProgress, is, os, scanStartRange, scanEndRange); - HttpResponse response = execute(Method.POST, getRegion(bucketName), bucketName, objectName, - headerMap, queryParamMap, request, 0); + HttpResponse response = executePost(bucketName, objectName, headerMap, queryParamMap, request); return new SelectResponseStream(response.body().byteStream()); } @@ -6556,6 +6272,86 @@ private int getAvailableSize(Object inputStream, int expectedReadSize) throws IO } + /** + * Sets HTTP connect, write and read timeouts. A value of 0 means no timeout, otherwise values must be between 1 and + * Integer.MAX_VALUE when converted to milliseconds. + * + *

Example:
+ *
{@code minioClient.setTimeout(TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(10),
+   *                            TimeUnit.SECONDS.toMillis(30)); }
+ * + * @param connectTimeout HTTP connect timeout in milliseconds. + * @param writeTimeout HTTP write timeout in milliseconds. + * @param readTimeout HTTP read timeout in milliseconds. + */ + public void setTimeout(long connectTimeout, long writeTimeout, long readTimeout) { + this.httpClient = this.httpClient.newBuilder() + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + + /** + * Ignores check on server certificate for HTTPS connection. + * + *

Example:
+ *
{@code minioClient.ignoreCertCheck(); }
+ * + */ + @SuppressFBWarnings(value = "SIC", justification = "Should not be used in production anyways.") + public void ignoreCertCheck() throws NoSuchAlgorithmException, KeyManagementException { + final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + this.httpClient = this.httpClient.newBuilder() + .sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]) + .hostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }) + .build(); + } + + + /** + * Sets application's name/version to user agent. For more information about user agent + * refer #rfc2616. + * + * @param name Your application name. + * @param version Your application version. + */ + @SuppressWarnings("unused") + public void setAppInfo(String name, String version) { + if (name == null || version == null) { + // nothing to do + return; + } + + this.userAgent = DEFAULT_USER_AGENT + " " + name.trim() + "/" + version.trim(); + } + + /** * Enables HTTP call tracing and written to traceStream. * diff --git a/api/src/main/java/io/minio/Result.java b/api/src/main/java/io/minio/Result.java index 6972638c0..b4c9c47e7 100644 --- a/api/src/main/java/io/minio/Result.java +++ b/api/src/main/java/io/minio/Result.java @@ -27,6 +27,7 @@ import io.minio.errors.ErrorResponseException; import io.minio.errors.InsufficientDataException; import io.minio.errors.InternalException; +import io.minio.errors.InvalidArgumentException; import io.minio.errors.InvalidBucketNameException; import io.minio.errors.NoResponseException; @@ -49,7 +50,7 @@ public Result(T type, Exception ex) { * Returns given Type if exception is null, else respective exception is thrown. */ public T get() - throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, + throws InvalidBucketNameException, InvalidArgumentException, NoSuchAlgorithmException, InsufficientDataException, JsonParseException, JsonMappingException,IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, InternalException { @@ -61,6 +62,10 @@ public T get() throw (InvalidBucketNameException) ex; } + if (ex instanceof InvalidArgumentException) { + throw (InvalidArgumentException) ex; + } + if (ex instanceof NoSuchAlgorithmException) { throw (NoSuchAlgorithmException) ex; } diff --git a/functional/FunctionalTest.java b/functional/FunctionalTest.java index 6f8555af7..76cd5bbdd 100644 --- a/functional/FunctionalTest.java +++ b/functional/FunctionalTest.java @@ -1628,7 +1628,7 @@ public static void listIncompleteUploads_test1() throws Exception { if (!e.errorResponse().code().equals("IncompleteBody")) { throw e; } - } catch (java.net.ProtocolException e) { + } catch (InsufficientDataException e) { ignore(); } @@ -1666,7 +1666,7 @@ public static void listIncompleteUploads_test2() throws Exception { if (!e.errorResponse().code().equals("IncompleteBody")) { throw e; } - } catch (java.net.ProtocolException e) { + } catch (InsufficientDataException e) { ignore(); } @@ -1706,7 +1706,7 @@ public static void listIncompleteUploads_test3() throws Exception { if (!e.errorResponse().code().equals("IncompleteBody")) { throw e; } - } catch (java.net.ProtocolException e) { + } catch (InsufficientDataException e) { ignore(); } @@ -1746,7 +1746,7 @@ public static void removeIncompleteUploads_test() throws Exception { if (!e.errorResponse().code().equals("IncompleteBody")) { throw e; } - } catch (java.net.ProtocolException e) { + } catch (InsufficientDataException e) { ignore(); }