diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java
index c6ee258c064e..794245dea8d9 100644
--- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java
+++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtS3V4aSigner.java
@@ -38,10 +38,13 @@
*
* See
* Amazon S3 Sigv4 documentation for more detailed information.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
+@Deprecated
public interface AwsCrtS3V4aSigner extends Signer, Presigner {
/**
diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java
index 692ecec5047f..e43425d83db6 100644
--- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java
+++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSigner.java
@@ -28,10 +28,13 @@
* (Common RunTime) library.
*
* In CRT signing, payload signing is the default unless an override value is specified.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
+@Deprecated
public interface AwsCrtV4aSigner extends Signer, Presigner {
/**
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AsyncAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AsyncAws4Signer.java
index 5a175bcaf4b4..eb5a6b4339db 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AsyncAws4Signer.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AsyncAws4Signer.java
@@ -34,7 +34,10 @@
/**
* AWS Signature Version 4 signer that can include contents of an asynchronous request body into the signature
* calculation.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
+@Deprecated
@SdkPublicApi
public final class AsyncAws4Signer extends BaseAws4Signer implements AsyncSigner {
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java
index bad5428c3a8c..7c37904ecfe1 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java
@@ -20,7 +20,10 @@
/**
* Signer implementation that signs requests with the AWS4 signing protocol.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
+@Deprecated
@SdkPublicApi
public final class Aws4Signer extends BaseAws4Signer {
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java
index 5b49c694dc3a..9773d81d7645 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java
@@ -32,8 +32,11 @@
*
* Payloads are still signed for requests over HTTP to preserve the request
* integrity over a non-secure transport.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
+@Deprecated
public final class Aws4UnsignedPayloadSigner extends BaseAws4Signer {
public static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java
index eb0a93c123f4..9df0b58b9302 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java
@@ -20,7 +20,10 @@
/**
* AWS4 signer implementation for AWS S3
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
+@Deprecated
@SdkPublicApi
public final class AwsS3V4Signer extends AbstractAwsS3V4Signer {
private AwsS3V4Signer() {
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java
index 474046cf2202..8776f4b5dc0b 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java
@@ -18,6 +18,7 @@
import software.amazon.awssdk.annotations.SdkProtectedApi;
import software.amazon.awssdk.auth.signer.internal.BaseEventStreamAsyncAws4Signer;
+@Deprecated
@SdkProtectedApi
public final class EventStreamAws4Signer extends BaseEventStreamAsyncAws4Signer {
private EventStreamAws4Signer() {
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/SignerLoader.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/SignerLoader.java
index add5a3e83208..94781f69b90f 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/SignerLoader.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/SignerLoader.java
@@ -26,6 +26,7 @@
/**
* Utility class for instantiating signers only if they're available on the class path.
*/
+@Deprecated
@SdkProtectedApi
public final class SignerLoader {
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java
index 6ae237487578..08652ec79134 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java
@@ -27,6 +27,7 @@
* Abstract base class for concrete implementations of Aws4 signers.
*/
@SdkInternalApi
+@Deprecated
public abstract class BaseAws4Signer extends AbstractAws4Signer {
@Override
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/token/signer/aws/BearerTokenSigner.java b/core/auth/src/main/java/software/amazon/awssdk/auth/token/signer/aws/BearerTokenSigner.java
index f150ac057fe1..9d8e6040ccdd 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/token/signer/aws/BearerTokenSigner.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/token/signer/aws/BearerTokenSigner.java
@@ -27,8 +27,11 @@
/**
* A {@link Signer} that will sign a request with Bearer token authorization.
+ *
+ * @deprecated Use {@code software.amazon.awssdk.http.auth.signer.BearerHttpSigner} from the 'http-auth' module.
*/
@SdkPublicApi
+@Deprecated
public final class BearerTokenSigner implements Signer {
private static final String BEARER_LABEL = "Bearer";
diff --git a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java
index dd6ebdf72760..5041047cb509 100644
--- a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java
+++ b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java
@@ -29,7 +29,8 @@
@SdkPublicApi
public interface AwsV4FamilyHttpSigner extends HttpSigner {
/**
- * The name of the AWS service. This property is required.
+ * The name of the AWS service. This property is required. This value can be found in the service documentation or
+ * on the service client itself (e.g. {@code S3Client.SERVICE_NAME}).
*/
SignerProperty SERVICE_SIGNING_NAME =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "ServiceSigningName");
@@ -37,6 +38,8 @@ public interface AwsV4FamilyHttpSigner extends HttpSigner
/**
* A boolean to indicate whether to double url-encode the resource path when constructing the canonical request. This property
* defaults to true.
+ *
+ * Note: S3 requires this value to be set to 'false' to prevent signature mismatch errors for certain paths.
*/
SignerProperty DOUBLE_URL_ENCODE =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "DoubleUrlEncode");
@@ -44,6 +47,8 @@ public interface AwsV4FamilyHttpSigner extends HttpSigner
/**
* A boolean to indicate whether the resource path should be "normalized" according to RFC3986 when constructing the canonical
* request. This property defaults to true.
+ *
+ * Note: S3 requires this value to be set to 'false' to prevent signature mismatch errors for certain paths.
*/
SignerProperty NORMALIZE_PATH =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "NormalizePath");
@@ -64,13 +69,21 @@ public interface AwsV4FamilyHttpSigner extends HttpSigner
/**
* Whether to indicate that a payload is signed or not. This property defaults to true. This can be set false to disable
* payload signing.
+ *
+ * When this value is true and {@link #CHUNK_ENCODING_ENABLED} is false, the whole payload must be read to generate
+ * the payload signature. For very large payloads, this could impact memory usage and call latency. Some services
+ * support this value being disabled, especially over HTTPS where SSL provides some of its own protections against
+ * payload tampering.
*/
SignerProperty PAYLOAD_SIGNING_ENABLED =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "PayloadSigningEnabled");
/**
* Whether to indicate that a payload is chunk-encoded or not. This property defaults to false. This can be set true to
- * enable the `aws-chunk` content-encoding
+ * enable the `aws-chunked` content-encoding.
+ *
+ * Only some services support this value being set to true, but for those services it can prevent the need to read
+ * the whole payload before writing when {@link #PAYLOAD_SIGNING_ENABLED} is true.
*/
SignerProperty CHUNK_ENCODING_ENABLED =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "ChunkEncodingEnabled");
diff --git a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4HttpSigner.java b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4HttpSigner.java
index 4b85c017c0f5..ee3656f946e4 100644
--- a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4HttpSigner.java
+++ b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4HttpSigner.java
@@ -22,10 +22,67 @@
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
/**
- * An {@link HttpSigner} that will sign a request using an AWS credentials {@link AwsCredentialsIdentity}).
+ * An {@link HttpSigner} that will use the AWS V4 signing algorithm to sign a request using an
+ * {@link AwsCredentialsIdentity}).
+ *
*
- * The process for signing requests to send to AWS services is documented
+ * The steps performed by this signer are documented
* here.
+ *
+ *
Using the AwsV4HttpSigner
+ *
+ * Sign an HTTP request and send it to a service.
+ *
+ * {@snippet :
+ * AwsV4HttpSigner signer = AwsV4HttpSigner.create();
+ *
+ * // Specify AWS credentials. Credential providers that are used by the SDK by default are
+ * // available in the module "auth" (e.g. DefaultCredentialsProvider).
+ * AwsCredentialsIdentity credentials =
+ * AwsSessionCredentialsIdentity.create("skid", "akid", "stok");
+ *
+ * // Create the HTTP request to be signed
+ * SdkHttpRequest httpRequest =
+ * SdkHttpRequest.builder()
+ * .uri("https://s3.us-west-2.amazonaws.com/bucket/object")
+ * .method(SdkHttpMethod.PUT)
+ * .putHeader("Content-Type", "text/plain")
+ * .build();
+ *
+ * // Create the request payload to be signed
+ * ContentStreamProvider requestPayload =
+ * ContentStreamProvider.fromUtf8String("Hello, World!");
+ *
+ * // Sign the request. Some services require custom signing configuration properties (e.g. S3).
+ * // See AwsV4HttpSigner and AwsV4FamilyHttpSigner for the available signing options.
+ * // Note: The S3Client class below requires a dependency on the 's3' module. Alternatively, the
+ * // signing name can be hard-coded because it is guaranteed to not change.
+ * SignedRequest signedRequest =
+ * signer.sign(r -> r.identity(credentials)
+ * .request(httpRequest)
+ * .payload(requestPayload)
+ * .putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, S3Client.SERVICE_NAME)
+ * .putProperty(AwsV4HttpSigner.REGION_NAME, "us-west-2")
+ * .putProperty(AwsV4HttpSigner.DOUBLE_URL_ENCODE, false) // Required for S3 only
+ * .putProperty(AwsV4HttpSigner.NORMALIZE_PATH, false)); // Required for S3 only
+ *
+ * // Create and HTTP client and send the request. ApacheHttpClient requires the 'apache-client' module.
+ * try (SdkHttpClient httpClient = ApacheHttpClient.create()) {
+ * HttpExecuteRequest httpExecuteRequest =
+ * HttpExecuteRequest.builder()
+ * .request(signedRequest.request())
+ * .contentStreamProvider(signedRequest.payload().orElse(null))
+ * .build();
+ *
+ * HttpExecuteResponse httpResponse =
+ * httpClient.prepareRequest(httpExecuteRequest).call();
+ *
+ * System.out.println("HTTP Status Code: " + httpResponse.httpResponse().statusCode());
+ * } catch (IOException e) {
+ * System.err.println("HTTP Request Failed.");
+ * e.printStackTrace();
+ * }
+ * }
*/
@SdkPublicApi
public interface AwsV4HttpSigner extends AwsV4FamilyHttpSigner {
diff --git a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4aHttpSigner.java b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4aHttpSigner.java
index 686ac70645aa..503d2de7e0ef 100644
--- a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4aHttpSigner.java
+++ b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4aHttpSigner.java
@@ -23,10 +23,67 @@
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
/**
- * An {@link HttpSigner} that will sign a request using an AWS credentials {@link AwsCredentialsIdentity}).
+ * An {@link HttpSigner} that will use the AWS V4a signing algorithm to sign a request using an
+ * {@link AwsCredentialsIdentity}).
+ *
*
- * The process for signing requests to send to AWS services is documented
+ * AWS request signing is described
* here.
+ *
+ *
Using the AwsV4aHttpSigner
+ *
+ * Sign an HTTP request and send it to a service.
+ *
+ * {@snippet :
+ * AwsV4aHttpSigner signer = AwsV4aHttpSigner.create();
+ *
+ * // Specify AWS credentials. Credential providers that are used by the SDK by default are
+ * // available in the module "auth" (e.g. DefaultCredentialsProvider).
+ * AwsCredentialsIdentity credentials =
+ * AwsSessionCredentialsIdentity.create("skid", "akid", "stok");
+ *
+ * // Create the HTTP request to be signed
+ * SdkHttpRequest httpRequest =
+ * SdkHttpRequest.builder()
+ * .uri("https://s3.us-west-2.amazonaws.com/bucket/object")
+ * .method(SdkHttpMethod.PUT)
+ * .putHeader("Content-Type", "text/plain")
+ * .build();
+ *
+ * // Create the request payload to be signed
+ * ContentStreamProvider requestPayload =
+ * ContentStreamProvider.fromUtf8String("Hello, World!");
+ *
+ * // Sign the request. Some services require custom signing configuration properties (e.g. S3).
+ * // See AwsV4aHttpSigner and AwsV4FamilyHttpSigner for the available signing options.
+ * // Note: The S3Client class below requires a dependency on the 's3' module. Alternatively, the
+ * // signing name can be hard-coded because it is guaranteed to not change.
+ * SignedRequest signedRequest =
+ * signer.sign(r -> r.identity(credentials)
+ * .request(httpRequest)
+ * .payload(requestPayload)
+ * .putProperty(AwsV4aHttpSigner.SERVICE_SIGNING_NAME, S3Client.SERVICE_NAME)
+ * .putProperty(AwsV4aHttpSigner.REGION_SET, RegionSet.create("us-west-2"))
+ * .putProperty(AwsV4aHttpSigner.DOUBLE_URL_ENCODE, false) // Required for S3 only
+ * .putProperty(AwsV4aHttpSigner.NORMALIZE_PATH, false)); // Required for S3 only
+ *
+ * // Create and HTTP client and send the request. ApacheHttpClient requires the 'apache-client' module.
+ * try (SdkHttpClient httpClient = ApacheHttpClient.create()) {
+ * HttpExecuteRequest httpExecuteRequest =
+ * HttpExecuteRequest.builder()
+ * .request(signedRequest.request())
+ * .contentStreamProvider(signedRequest.payload().orElse(null))
+ * .build();
+ *
+ * HttpExecuteResponse httpResponse =
+ * httpClient.prepareRequest(httpExecuteRequest).call();
+ *
+ * System.out.println("HTTP Status Code: " + httpResponse.httpResponse().statusCode());
+ * } catch (IOException e) {
+ * System.err.println("HTTP Request Failed.");
+ * e.printStackTrace();
+ * }
+ * }
*/
@SdkPublicApi
public interface AwsV4aHttpSigner extends AwsV4FamilyHttpSigner {
@@ -41,5 +98,4 @@ public interface AwsV4aHttpSigner extends AwsV4FamilyHttpSigner implements HttpSigner {
+ @Override
+ public SignedRequest sign(SignRequest extends T> request) {
+ return SignedRequest.builder()
+ .request(request.request())
+ .payload(request.payload().orElse(null))
+ .build();
+ }
+
+ @Override
+ public CompletableFuture signAsync(AsyncSignRequest extends T> request) {
+ return CompletableFuture.completedFuture(AsyncSignedRequest.builder()
+ .request(request.request())
+ .payload(request.payload().orElse(null))
+ .build());
+ }
+}
diff --git a/core/http-auth-spi/src/main/java/software/amazon/awssdk/http/auth/spi/signer/HttpSigner.java b/core/http-auth-spi/src/main/java/software/amazon/awssdk/http/auth/spi/signer/HttpSigner.java
index 5aafbe51e116..0f60bae1d7e0 100644
--- a/core/http-auth-spi/src/main/java/software/amazon/awssdk/http/auth/spi/signer/HttpSigner.java
+++ b/core/http-auth-spi/src/main/java/software/amazon/awssdk/http/auth/spi/signer/HttpSigner.java
@@ -21,6 +21,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.http.auth.spi.internal.signer.DefaultAsyncSignRequest;
import software.amazon.awssdk.http.auth.spi.internal.signer.DefaultSignRequest;
+import software.amazon.awssdk.http.auth.spi.internal.signer.NoOpHttpSigner;
import software.amazon.awssdk.identity.spi.Identity;
/**
@@ -31,7 +32,6 @@
*/
@SdkPublicApi
public interface HttpSigner {
-
/**
* A {@link Clock} to be used to derive the signing time. This property defaults to the system clock.
*
@@ -39,6 +39,13 @@ public interface HttpSigner {
*/
SignerProperty SIGNING_CLOCK = SignerProperty.create(HttpSigner.class, "SigningClock");
+ /**
+ * Retrieve a signer that returns the input message, without signing.
+ */
+ default HttpSigner doNotSign() {
+ return new NoOpHttpSigner<>();
+ }
+
/**
* Method that takes in inputs to sign a request with sync payload and returns a signed version of the request.
*
@@ -84,4 +91,5 @@ default SignedRequest sign(Consumer> consumer) {
default CompletableFuture signAsync(Consumer> consumer) {
return signAsync(DefaultAsyncSignRequest.builder().applyMutation(consumer).build());
}
+
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncRequestBodySigner.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncRequestBodySigner.java
index fca744c562de..e0ef03e70b03 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncRequestBodySigner.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncRequestBodySigner.java
@@ -22,9 +22,12 @@
/**
* Interface for the signer used for signing the async requests.
+ *
+ * @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner} in 'http-auth-spi'.
*/
@SdkPublicApi
@FunctionalInterface
+@Deprecated
public interface AsyncRequestBodySigner {
/**
* Method that takes in an signed request and async request body provider,
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncSigner.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncSigner.java
index ee31dfffbe91..e901242db74f 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncSigner.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/AsyncSigner.java
@@ -23,8 +23,11 @@
/**
* A signer capable of including the contents of the asynchronous body into the request calculation.
+ *
+ * @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner} in 'http-auth-spi'.
*/
@SdkPublicApi
+@Deprecated
public interface AsyncSigner {
/**
* Sign the request, including the contents of the body into the signature calculation.
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/NoOpSigner.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/NoOpSigner.java
index e6f2f96dd1d6..ae0576920998 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/NoOpSigner.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/NoOpSigner.java
@@ -22,8 +22,12 @@
/**
* A No op implementation of Signer and Presigner interfaces that returns the
* input {@link SdkHttpFullRequest} without modifications.
+ *
+ * @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner#doNotSign()} in
+ * 'http-auth-spi'.
*/
@SdkPublicApi
+@Deprecated
public final class NoOpSigner implements Signer, Presigner {
@Override
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Presigner.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Presigner.java
index f3eeda10be93..025e86dcf4d5 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Presigner.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Presigner.java
@@ -22,9 +22,12 @@
/**
* Interface for the signer used for pre-signing the requests. All SDK signer implementations that support pre-signing
* will implement this interface.
+ *
+ * @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner} in 'http-auth-spi'.
*/
@SdkPublicApi
@FunctionalInterface
+@Deprecated
public interface Presigner {
/**
* Method that takes in an request and returns a pre signed version of the request.
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Signer.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Signer.java
index c44adaa37089..e43b420d908c 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Signer.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/signer/Signer.java
@@ -22,9 +22,12 @@
/**
* Interface for the signer used for signing the requests. All SDK signer implementations will implement this interface.
+ *
+ * @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner} in 'http-auth-spi'.
*/
@SdkPublicApi
@FunctionalInterface
+@Deprecated
public interface Signer {
/**
* Method that takes in an request and returns a signed version of the request.
diff --git a/http-client-spi/src/main/java/software/amazon/awssdk/http/ContentStreamProvider.java b/http-client-spi/src/main/java/software/amazon/awssdk/http/ContentStreamProvider.java
index cbb63dc2faf2..59493fd33e4d 100644
--- a/http-client-spi/src/main/java/software/amazon/awssdk/http/ContentStreamProvider.java
+++ b/http-client-spi/src/main/java/software/amazon/awssdk/http/ContentStreamProvider.java
@@ -15,21 +15,119 @@
package software.amazon.awssdk.http;
+import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
+
+import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.utils.IoUtils;
+import software.amazon.awssdk.utils.StringInputStream;
+import software.amazon.awssdk.utils.Validate;
/**
* Provides the content stream of a request.
*
- * Each call to to the {@link #newStream()} method must result in a stream whose position is at the beginning of the content.
+ * Each call to the {@link #newStream()} method must result in a stream whose position is at the beginning of the content.
* Implementations may return a new stream or the same stream for each call. If returning a new stream, the implementation
* must ensure to {@code close()} and free any resources acquired by the previous stream. The last stream returned by {@link
* #newStream()}} will be closed by the SDK.
- *
*/
@SdkPublicApi
@FunctionalInterface
public interface ContentStreamProvider {
+ /**
+ * Create {@link ContentStreamProvider} from a byte array. This will copy the contents of the byte array.
+ */
+ static ContentStreamProvider fromByteArray(byte[] bytes) {
+ Validate.paramNotNull(bytes, "bytes");
+ byte[] copy = Arrays.copyOf(bytes, bytes.length);
+ return () -> new ByteArrayInputStream(copy);
+ }
+
+ /**
+ * Create {@link ContentStreamProvider} from a byte array without copying the contents of the byte array.
+ * This introduces concurrency risks, allowing the caller to modify the byte array stored in this
+ * {@code ContentStreamProvider} implementation.
+ *
+ *
As the method name implies, this is unsafe. Use {@link #fromByteArray(byte[])} unless you're sure you know
+ * the risks.
+ */
+ static ContentStreamProvider fromByteArrayUnsafe(byte[] bytes) {
+ Validate.paramNotNull(bytes, "bytes");
+ return () -> new ByteArrayInputStream(bytes);
+ }
+
+ /**
+ * Create {@link ContentStreamProvider} from a string, using the provided charset.
+ */
+ static ContentStreamProvider fromString(String string, Charset charset) {
+ Validate.paramNotNull(string, "string");
+ Validate.paramNotNull(charset, "charset");
+ return () -> new StringInputStream(string, charset);
+ }
+
+ /**
+ * Create {@link ContentStreamProvider} from a string, using the UTF-8 charset.
+ */
+ static ContentStreamProvider fromUtf8String(String string) {
+ return fromString(string, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Create a {@link ContentStreamProvider} from an input stream.
+ *
+ * If the provided input stream supports mark/reset, the stream will be marked with a 128Kb read limit and reset
+ * each time {@link #newStream()} is invoked. If the provided input stream does not support mark/reset,
+ * {@link #newStream()} will return the provided stream once, but fail subsequent calls. To create new streams when
+ * needed instead of using mark/reset, see {@link #fromInputStreamSupplier(Supplier)}.
+ */
+ static ContentStreamProvider fromInputStream(InputStream inputStream) {
+ Validate.paramNotNull(inputStream, "inputStream");
+ IoUtils.markStreamWithMaxReadLimit(inputStream);
+ return new ContentStreamProvider() {
+ private boolean first = true;
+ @Override
+ public InputStream newStream() {
+ if (first) {
+ first = false;
+ return inputStream;
+ }
+
+ if (inputStream.markSupported()) {
+ invokeSafely(inputStream::reset);
+ return inputStream;
+ }
+
+ throw new IllegalStateException("Content input stream does not support mark/reset, "
+ + "and was already read once.");
+ }
+ };
+ }
+
+ /**
+ * Create {@link ContentStreamProvider} from an input stream supplier. Each time a new stream is retrieved from
+ * this content stream provider, the last one returned will be closed.
+ */
+ static ContentStreamProvider fromInputStreamSupplier(Supplier inputStreamSupplier) {
+ Validate.paramNotNull(inputStreamSupplier, "inputStreamSupplier");
+ return new ContentStreamProvider() {
+ private InputStream lastStream;
+
+ @Override
+ public InputStream newStream() {
+ if (lastStream != null) {
+ invokeSafely(lastStream::close);
+ }
+ lastStream = inputStreamSupplier.get();
+ return lastStream;
+ }
+ };
+ }
+
/**
* @return The content stream.
*/
diff --git a/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpRequest.java b/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpRequest.java
index d011f9d7b19a..bb34909b5f36 100644
--- a/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpRequest.java
+++ b/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpRequest.java
@@ -191,6 +191,17 @@ default Builder uri(URI uri) {
return builder;
}
+ /**
+ * Convenience method to set the {@link #protocol()}, {@link #host()}, {@link #port()},
+ * {@link #encodedPath()} and extracts query parameters from a URI string.
+ *
+ * @param uri URI containing protocol, host, port and path.
+ * @return This builder for method chaining.
+ */
+ default Builder uri(String uri) {
+ return uri(URI.create(uri));
+ }
+
/**
* The protocol, exactly as it was configured with {@link #protocol(String)}.
*/
diff --git a/http-client-spi/src/test/java/software/amazon/awssdk/http/ContentStreamProviderTest.java b/http-client-spi/src/test/java/software/amazon/awssdk/http/ContentStreamProviderTest.java
new file mode 100644
index 000000000000..1298360f753b
--- /dev/null
+++ b/http-client-spi/src/test/java/software/amazon/awssdk/http/ContentStreamProviderTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.http;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import software.amazon.awssdk.utils.IoUtils;
+import software.amazon.awssdk.utils.StringInputStream;
+
+class ContentStreamProviderTest {
+ @Test
+ void fromMethods_failOnNull() {
+ assertThatThrownBy(() -> ContentStreamProvider.fromByteArray(null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromByteArrayUnsafe(null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromString("foo", null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromString(null, StandardCharsets.UTF_8)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromUtf8String(null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromInputStream(null)).isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> ContentStreamProvider.fromInputStreamSupplier(null)).isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ void fromByteArray_containsInputBytes() throws IOException {
+ byte[] bytes = "foo".getBytes();
+ ContentStreamProvider provider = ContentStreamProvider.fromByteArray(bytes);
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ }
+
+ @Test
+ void fromByteArray_doesNotAllowModifyingInputBytes() throws IOException {
+ byte[] bytes = "foo".getBytes();
+ byte[] bytesCopy = Arrays.copyOf(bytes, bytes.length);
+
+ ContentStreamProvider provider = ContentStreamProvider.fromByteArray(bytes);
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ bytes[0] = 'b';
+ assertArrayEquals(bytesCopy, IoUtils.toByteArray(provider.newStream()));
+ }
+
+ @Test
+ void fromByteArrayUnsafe_containsInputBytes() throws IOException {
+ byte[] bytes = "foo".getBytes();
+ ContentStreamProvider provider = ContentStreamProvider.fromByteArray(bytes);
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ }
+
+ @Test
+ void fromByteArrayUnsafe_doesNotProtectAgainstModifyingInputBytes() throws IOException {
+ byte[] bytes = "foo".getBytes();
+
+ ContentStreamProvider provider = ContentStreamProvider.fromByteArrayUnsafe(bytes);
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ bytes[0] = 'b';
+ assertArrayEquals(bytes, IoUtils.toByteArray(provider.newStream()));
+ }
+
+ @Test
+ void fromString_containsInputBytes() throws IOException {
+ String str = "foo";
+ ContentStreamProvider provider = ContentStreamProvider.fromString(str, StandardCharsets.UTF_8);
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ }
+
+ @Test
+ void fromString_honorsEncoding() throws IOException {
+ String str = "\uD83D\uDE0A";
+ ContentStreamProvider asciiProvider = ContentStreamProvider.fromString(str, StandardCharsets.US_ASCII);
+ ContentStreamProvider utf8Provider = ContentStreamProvider.fromString(str, StandardCharsets.UTF_8);
+ assertArrayEquals("?".getBytes(StandardCharsets.US_ASCII), IoUtils.toByteArray(asciiProvider.newStream()));
+ assertArrayEquals("\uD83D\uDE0A".getBytes(StandardCharsets.UTF_8), IoUtils.toByteArray(utf8Provider.newStream()));
+ }
+
+ @Test
+ void fromUtf8String_containsInputBytes() throws IOException {
+ String str = "foo";
+ ContentStreamProvider provider = ContentStreamProvider.fromUtf8String(str);
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ }
+
+ @Test
+ void fromInputStream_containsInputBytes() throws IOException {
+ String str = "foo";
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStream(new StringInputStream(str));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ }
+
+ @Test
+ void fromInputStream_failsOnSecondNewStream_ifInputStreamDoesNotSupportMarkAndReset() {
+ InputStream stream = new InputStream() {
+ @Override
+ public int read() {
+ return 0;
+ }
+ };
+
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStream(stream);
+ provider.newStream();
+ assertThatThrownBy(provider::newStream).isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("reset");
+ }
+
+ @Test
+ void fromInputStream_marksStream() throws IOException {
+ InputStream stream = Mockito.mock(InputStream.class);
+ Mockito.when(stream.markSupported()).thenReturn(true);
+
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStream(stream);
+ provider.newStream().read();
+ Mockito.verify(stream, Mockito.atLeastOnce()).mark(Mockito.anyInt());
+ }
+
+ @Test
+ void fromInputStream_doesNotCloseProvidedResettableStream() throws IOException {
+ InputStream stream = Mockito.mock(InputStream.class);
+ Mockito.when(stream.markSupported()).thenReturn(true);
+
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStream(stream);
+ provider.newStream().read();
+ provider.newStream().read();
+ Mockito.verify(stream, Mockito.never()).close();
+ }
+
+ @Test
+ void fromInputStream_doesNotCloseProvidedSingleUseStream() throws IOException {
+ InputStream stream = Mockito.mock(InputStream.class);
+ Mockito.when(stream.markSupported()).thenReturn(false);
+
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStream(stream);
+ provider.newStream().read();
+ Mockito.verify(stream, Mockito.never()).close();
+ }
+
+ @Test
+ void fromInputStreamSupplier_containsInputBytes() throws IOException {
+ String str = "foo";
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStreamSupplier(() -> new StringInputStream(str));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ assertEquals(str, IoUtils.toUtf8String(provider.newStream()));
+ }
+
+ @Test
+ void fromInputStreamSupplier_closesStreams() throws IOException {
+ InputStream stream1 = Mockito.mock(InputStream.class);
+ InputStream stream2 = Mockito.mock(InputStream.class);
+ Supplier streamSupplier = Mockito.mock(Supplier.class);
+
+ Mockito.when(streamSupplier.get()).thenReturn(stream1, stream2);
+
+ ContentStreamProvider provider = ContentStreamProvider.fromInputStreamSupplier(streamSupplier);
+
+ provider.newStream();
+
+ Mockito.verify(stream1, Mockito.never()).close();
+ Mockito.verify(stream2, Mockito.never()).close();
+
+ provider.newStream();
+
+ Mockito.verify(stream1, Mockito.times(1)).close();
+ Mockito.verify(stream2, Mockito.never()).close();
+ }
+}
\ No newline at end of file
diff --git a/utils/src/main/java/software/amazon/awssdk/utils/StringInputStream.java b/utils/src/main/java/software/amazon/awssdk/utils/StringInputStream.java
index f0d595c681e3..36da50fbce5d 100644
--- a/utils/src/main/java/software/amazon/awssdk/utils/StringInputStream.java
+++ b/utils/src/main/java/software/amazon/awssdk/utils/StringInputStream.java
@@ -16,6 +16,7 @@
package software.amazon.awssdk.utils;
import java.io.ByteArrayInputStream;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import software.amazon.awssdk.annotations.SdkProtectedApi;
@@ -29,7 +30,11 @@ public class StringInputStream extends ByteArrayInputStream {
private final String string;
public StringInputStream(String s) {
- super(s.getBytes(StandardCharsets.UTF_8));
+ this(s, StandardCharsets.UTF_8);
+ }
+
+ public StringInputStream(String s, Charset charset) {
+ super(s.getBytes(charset));
this.string = s;
}