From 03962e2a7e78eed446c8e64adb0bb03e8fa1fb4a Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Tue, 31 Mar 2026 17:29:45 -0500 Subject: [PATCH 1/4] Replace builders with modifiable request/response --- .../client/auth/scheme/sigv4/SigV4Signer.java | 5 +- .../aws/client/http/AmzSdkRequestPlugin.java | 5 +- .../client/http/RecursionDetectionPlugin.java | 5 +- .../http/RecursionDetectionPluginTest.java | 3 +- .../client/core/interceptors/RequestHook.java | 2 +- .../client/http/HttpErrorDeserializer.java | 9 ++-- .../http/auth/HttpApiKeyAuthSigner.java | 16 +++--- .../client/http/auth/HttpBasicAuthSigner.java | 10 ++-- .../http/auth/HttpBearerAuthSigner.java | 10 ++-- .../http/plugins/HttpChecksumPlugin.java | 7 ++- .../plugins/RequestCompressionPlugin.java | 7 ++- .../http/auth/HttpApiKeyAuthSignerTest.java | 10 ++-- .../codegen/client/GenericClientTest.java | 2 +- .../java/codegen/client/TestAuthScheme.java | 4 +- .../smithy/java/example/ClientExample.java | 4 +- .../smithy/java/http/api/HttpRequest.java | 14 ------ .../smithy/java/http/api/HttpResponse.java | 13 ----- .../java/http/api/ModifiableHttpMessage.java | 50 +++++++++++++++++-- .../java/http/api/ModifiableHttpRequest.java | 13 +++-- .../http/api/ModifiableHttpRequestImpl.java | 15 ++++-- .../java/http/api/ModifiableHttpResponse.java | 5 +- .../http/api/ModifiableHttpResponseImpl.java | 12 +++-- .../java/http/api/SmithyHttpMessageTest.java | 9 ++-- 23 files changed, 121 insertions(+), 109 deletions(-) diff --git a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java index 010860dd9..f04ddc451 100644 --- a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java +++ b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java @@ -91,8 +91,9 @@ public SignResult sign(HttpRequest request, AwsCredentialsIdentity identity.sessionToken(), !request.body().hasKnownLength()); var signedHeaders = signatureAndSignedHeaders.right; - return new SignResult<>(request.toBuilder().headers(HttpHeaders.of(signedHeaders)).build(), - signatureAndSignedHeaders.left); + var mod = request.toModifiableCopy(); + mod.setHeaders(HttpHeaders.of(signedHeaders).toModifiable()); + return new SignResult<>(mod, signatureAndSignedHeaders.left); } private String getPayloadHash(DataStream dataStream) { diff --git a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java index 70873bf06..d6750e15b 100644 --- a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java +++ b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java @@ -5,7 +5,6 @@ package software.amazon.smithy.java.aws.client.http; -import java.util.List; import software.amazon.smithy.java.client.core.CallContext; import software.amazon.smithy.java.client.core.ClientConfig; import software.amazon.smithy.java.client.core.ClientPlugin; @@ -38,9 +37,7 @@ public RequestT modifyBeforeSigning(RequestHook hook) value.append("; max=").append(max); } return hook.asRequestType( - req.toBuilder() - .withReplacedHeader("amz-sdk-request", List.of(value.toString())) - .build()); + req.toModifiableCopy().setHeader("amz-sdk-request", value.toString())); } } return hook.request(); diff --git a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java index ce85b325d..1b02e064a 100644 --- a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java +++ b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java @@ -48,8 +48,9 @@ private record Interceptor(List traceIdHeader) implements ClientIntercep public RequestT modifyBeforeTransmit(RequestHook hook) { if (hook.request() instanceof HttpRequest req) { if (!req.headers().hasHeader("x-amzn-trace-id")) { - return hook.asRequestType( - req.toBuilder().withReplacedHeader("x-amzn-trace-id", traceIdHeader).build()); + var mod = req.toModifiableCopy(); + mod.headers().setHeader("x-amzn-trace-id", traceIdHeader); + return hook.asRequestType(mod); } } return hook.request(); diff --git a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java index bbd13539b..f7ca9aa59 100644 --- a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java +++ b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java @@ -9,7 +9,6 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; -import java.util.List; import org.junit.jupiter.api.Test; import software.amazon.smithy.java.aws.client.awsjson.AwsJson1Protocol; import software.amazon.smithy.java.client.core.ClientPlugin; @@ -80,7 +79,7 @@ public void doesNotReplaceExistingValue() { public RequestT modifyBeforeSigning(RequestHook hook) { if (hook.request() instanceof HttpRequest req) { return hook.asRequestType( - req.toBuilder().withReplacedHeader("x-amzn-trace-id", List.of("hi")).build()); + req.toModifiableCopy().setHeader("x-amzn-trace-id", "hi")); } return hook.request(); } diff --git a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/interceptors/RequestHook.java b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/interceptors/RequestHook.java index 0f1a2671e..7852e1953 100644 --- a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/interceptors/RequestHook.java +++ b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/interceptors/RequestHook.java @@ -53,7 +53,7 @@ public RequestHook withRequest(RequestT request) { *

This is useful when modifying the request using {@code instanceof} pattern matching: * {@snippet : * if (hook.request() instanceof HttpRequest req) { - * return hook.asRequestType(req.toBuilder().withAddedHeader("X-Foo", "Bar").build()); + * return hook.asRequestType(req.toModifiableCopy().addHeader("X-Foo", "Bar")); * } * return hook.request(); * } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java index cb40b5156..304074f96 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java @@ -104,12 +104,9 @@ default ModeledException createErrorFromDocument( Document parsedDocument, ShapeBuilder builder ) { - return createError( - context, - codec, - // Make a new response that uses the previously read response payload. - response.toBuilder().body(DataStream.ofByteBuffer(responsePayload)).build(), - builder); + var modResponse = response.toModifiableCopy(); + modResponse.setBody(DataStream.ofByteBuffer(responsePayload)); + return createError(context, codec, modResponse, builder); } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java index 3dc73e44f..0b3fbf80b 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java @@ -5,13 +5,10 @@ package software.amazon.smithy.java.client.http.auth; -import java.util.LinkedHashMap; -import java.util.List; import software.amazon.smithy.java.auth.api.SignResult; import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.auth.api.identity.ApiKeyIdentity; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.io.uri.QueryStringBuilder; import software.amazon.smithy.java.logging.InternalLogger; @@ -29,16 +26,14 @@ public SignResult sign(HttpRequest request, ApiKeyIdentity identity case HEADER -> { var schemeValue = properties.get(HttpApiKeyAuthScheme.SCHEME); var value = identity.apiKey(); - // If the scheme value is not null prefix with scheme if (schemeValue != null) { value = schemeValue + " " + value; } - var updated = new LinkedHashMap<>(request.headers().map()); - var existing = updated.put(name, List.of(value)); - if (existing != null) { + var mod = request.toModifiableCopy(); + if (mod.headers().hasHeader(name)) { LOGGER.debug("Replaced header value for {}", name); } - yield new SignResult<>(request.toBuilder().headers(HttpHeaders.of(updated)).build()); + yield new SignResult<>(mod.setHeader(name, value)); } case QUERY -> { var queryBuilder = new QueryStringBuilder(); @@ -47,8 +42,9 @@ public SignResult sign(HttpRequest request, ApiKeyIdentity identity var existingQuery = request.uri().getQuery(); addExistingQueryParams(stringBuilder, existingQuery, name); queryBuilder.write(stringBuilder); - yield new SignResult<>( - request.toBuilder().uri(request.uri().withQuery(stringBuilder.toString())).build()); + var mod = request.toModifiableCopy(); + mod.setUri(request.uri().withQuery(stringBuilder.toString())); + yield new SignResult<>(mod); } }; } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java index be166ca5d..84e132efa 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java @@ -7,13 +7,10 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; -import java.util.LinkedHashMap; -import java.util.List; import software.amazon.smithy.java.auth.api.SignResult; import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.auth.api.identity.LoginIdentity; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.logging.InternalLogger; @@ -29,11 +26,10 @@ private HttpBasicAuthSigner() {} public SignResult sign(HttpRequest request, LoginIdentity identity, Context properties) { var identityString = identity.username() + ":" + identity.password(); var base64Value = Base64.getEncoder().encodeToString(identityString.getBytes(StandardCharsets.UTF_8)); - var headers = new LinkedHashMap<>(request.headers().map()); - var existing = headers.put(AUTHORIZATION_HEADER, List.of(SCHEME + " " + base64Value)); - if (existing != null) { + var mod = request.toModifiableCopy(); + if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { LOGGER.debug("Replaced existing Authorization header value."); } - return new SignResult<>(request.toBuilder().headers(HttpHeaders.of(headers)).build()); + return new SignResult<>(mod.setHeader(AUTHORIZATION_HEADER, SCHEME + " " + base64Value)); } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java index 91b012176..bf280a968 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java @@ -5,13 +5,10 @@ package software.amazon.smithy.java.client.http.auth; -import java.util.LinkedHashMap; -import java.util.List; import software.amazon.smithy.java.auth.api.SignResult; import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.auth.api.identity.TokenIdentity; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.logging.InternalLogger; @@ -25,11 +22,10 @@ private HttpBearerAuthSigner() {} @Override public SignResult sign(HttpRequest request, TokenIdentity identity, Context properties) { - var headers = new LinkedHashMap<>(request.headers().map()); - var existing = headers.put(AUTHORIZATION_HEADER, List.of(SCHEME + " " + identity.token())); - if (existing != null) { + var mod = request.toModifiableCopy(); + if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { LOGGER.debug("Replaced existing Authorization header value."); } - return new SignResult<>(request.toBuilder().headers(HttpHeaders.of(headers)).build()); + return new SignResult<>(mod.setHeader(AUTHORIZATION_HEADER, SCHEME + " " + identity.token())); } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java index aa6db4bea..aa3a73ec7 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java @@ -17,7 +17,6 @@ import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.io.ByteBufferUtils; import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait; -import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.SmithyInternalApi; /** @@ -55,9 +54,9 @@ static HttpRequest addContentMd5Header(HttpRequest request) { try { byte[] hash = MessageDigest.getInstance("MD5").digest(bytes); String base64Hash = Base64.getEncoder().encodeToString(hash); - return request.toBuilder() - .withReplacedHeader("Content-MD5", ListUtils.of(base64Hash)) - .build(); + var modifiable = request.toModifiable(); + modifiable.headers().setHeader("content-md5", base64Hash); + return modifiable; } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("Unable to fetch message digest instance for MD5", e); } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java index 424c1b75d..5b3506efe 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java @@ -56,10 +56,9 @@ public RequestT modifyBeforeRetryLoop(RequestHook hoo if (algorithmId.equals(algorithm.algorithmId())) { var compressed = algorithm.compress(req.body()); return hook.asRequestType( - req.toBuilder() - .body(compressed) - .withAddedHeader(CONTENT_ENCODING_HEADER, algorithmId) - .build()); + req.toModifiableCopy() + .setBody(compressed) + .addHeader(CONTENT_ENCODING_HEADER, algorithmId)); } } } diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java index 54f8ad359..b3e0971c9 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java @@ -57,7 +57,8 @@ void testOverwritesExistingHeader() { authProperties.put(HttpApiKeyAuthScheme.NAME, "x-api-key"); authProperties.put(HttpApiKeyAuthScheme.SCHEME, "SCHEME"); - var updateRequest = TEST_REQUEST.toBuilder().withAddedHeader("x-api-key", "foo").build(); + var updateRequest = TEST_REQUEST.toModifiableCopy(); + updateRequest.headers().addHeader("x-api-key", "foo"); var signedRequest = HttpApiKeyAuthSigner.INSTANCE.sign(updateRequest, TEST_IDENTITY, authProperties).signedRequest(); var authHeader = signedRequest.headers().firstValue("x-api-key"); @@ -97,7 +98,8 @@ void testApiKeyAuthSignerAddsQueryParamsAppendsToExisting() { authProperties.put(HttpApiKeyAuthScheme.IN, HttpApiKeyAuthTrait.Location.QUERY); authProperties.put(HttpApiKeyAuthScheme.NAME, "apiKey"); - var updatedRequest = TEST_REQUEST.toBuilder().uri(SmithyUri.of("https://www.example.com?x=1")).build(); + var updatedRequest = TEST_REQUEST.toModifiableCopy(); + updatedRequest.setUri(SmithyUri.of("https://www.example.com?x=1")); var signedRequest = HttpApiKeyAuthSigner.INSTANCE.sign(updatedRequest, TEST_IDENTITY, authProperties).signedRequest(); @@ -112,8 +114,8 @@ void testOverwritesExistingQuery() { authProperties.put(HttpApiKeyAuthScheme.IN, HttpApiKeyAuthTrait.Location.QUERY); authProperties.put(HttpApiKeyAuthScheme.NAME, "apiKey"); - var updatedRequest = - TEST_REQUEST.toBuilder().uri(SmithyUri.of("https://www.example.com?x=1&apiKey=foo")).build(); + var updatedRequest = TEST_REQUEST.toModifiableCopy(); + updatedRequest.setUri(SmithyUri.of("https://www.example.com?x=1&apiKey=foo")); var signedRequest = HttpApiKeyAuthSigner.INSTANCE.sign(updatedRequest, TEST_IDENTITY, authProperties).signedRequest(); diff --git a/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/client/GenericClientTest.java b/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/client/GenericClientTest.java index b2b62ea10..1040289f8 100644 --- a/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/client/GenericClientTest.java +++ b/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/client/GenericClientTest.java @@ -65,7 +65,7 @@ public void supportsInterceptors() { @Override public RequestT modifyBeforeTransmit(RequestHook hook) { if (hook.request() instanceof HttpRequest req) { - return hook.asRequestType(req.toBuilder().withAddedHeader("X-Foo", "Bar").build()); + return hook.asRequestType(req.toModifiableCopy().addHeader("X-Foo", "Bar")); } return hook.request(); } diff --git a/codegen/codegen-plugin/src/test/java/software/amazon/smithy/java/codegen/client/TestAuthScheme.java b/codegen/codegen-plugin/src/test/java/software/amazon/smithy/java/codegen/client/TestAuthScheme.java index dc324a23f..240656bb3 100644 --- a/codegen/codegen-plugin/src/test/java/software/amazon/smithy/java/codegen/client/TestAuthScheme.java +++ b/codegen/codegen-plugin/src/test/java/software/amazon/smithy/java/codegen/client/TestAuthScheme.java @@ -54,8 +54,8 @@ public Signer signer() { private static final class TestSigner implements Signer { @Override public SignResult sign(HttpRequest request, Identity identity, Context properties) { - return new SignResult<>( - request.toBuilder().withAddedHeader(SIGNATURE_HEADER, "smithy-test-signature").build()); + var mod = request.toModifiableCopy().addHeader(SIGNATURE_HEADER, "smithy-test-signature"); + return new SignResult<>(mod); } } diff --git a/examples/restjson-client/src/main/java/software/amazon/smithy/java/example/ClientExample.java b/examples/restjson-client/src/main/java/software/amazon/smithy/java/example/ClientExample.java index ea16e2565..642280822 100644 --- a/examples/restjson-client/src/main/java/software/amazon/smithy/java/example/ClientExample.java +++ b/examples/restjson-client/src/main/java/software/amazon/smithy/java/example/ClientExample.java @@ -130,7 +130,9 @@ public void readBeforeTransmit(RequestHook hook) { @Override public RequestT modifyBeforeTransmit(RequestHook hook) { if (hook.request() instanceof HttpRequest req) { - return hook.asRequestType(req.toBuilder().withAddedHeader("X-Foo", "Bar").build()); + var r = req.toModifiable(); + r.headers().setHeader("x-foo", "Bar"); + return hook.asRequestType(r); } return hook.request(); } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java index ec122e9b0..7107cb134 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java @@ -47,20 +47,6 @@ public interface HttpRequest extends HttpMessage { */ HttpRequest toUnmodifiable(); - /** - * Create a builder configured with the values of the request. - * - * @return the created builder. - */ - default Builder toBuilder() { - return builder() - .method(method()) - .uri(uri()) - .headers(headers()) - .body(body()) - .httpVersion(httpVersion()); - } - /** * Create a builder. * diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java index 0b65e69bc..0fb3d0391 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java @@ -37,19 +37,6 @@ public interface HttpResponse extends HttpMessage { */ HttpResponse toUnmodifiable(); - /** - * Create a builder configured with the values of the response. - * - * @return the created builder. - */ - default Builder toBuilder() { - return builder() - .httpVersion(httpVersion()) - .statusCode(statusCode()) - .headers(headers()) - .body(body()); - } - /** * Create a builder. * diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java index b7a04e5a8..609f490bb 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java @@ -5,12 +5,17 @@ package software.amazon.smithy.java.http.api; +import java.nio.ByteBuffer; +import java.util.concurrent.Flow; import software.amazon.smithy.java.io.datastream.DataStream; /** * A modifiable HTTP message. + * + * @param The concrete modifiable message type for fluent returns. */ -public interface ModifiableHttpMessage extends HttpMessage { +@SuppressWarnings("unchecked") +public interface ModifiableHttpMessage> extends HttpMessage { @Override ModifiableHttpHeaders headers(); @@ -18,15 +23,41 @@ public interface ModifiableHttpMessage extends HttpMessage { * Set the HTTP version. * * @param version Version to set. + * @return this message. */ - void setHttpVersion(HttpVersion version); + T setHttpVersion(HttpVersion version); /** * Set the HTTP headers. * * @param headers Headers to set. + * @return this message. */ - void setHeaders(ModifiableHttpHeaders headers); + T setHeaders(ModifiableHttpHeaders headers); + + /** + * Set a specific header by name and replace any existing value. + * + * @param name Header to set. + * @param value Value to set. + * @return modifiable message. + */ + default T setHeader(String name, String value) { + headers().setHeader(name, value); + return (T) this; + } + + /** + * Add a specific header by name to any existing headers with the same name. + * + * @param name Header to add. + * @param value Value to add. + * @return modifiable message. + */ + default T addHeader(String name, String value) { + headers().addHeader(name, value); + return (T) this; + } /** * Set the HTTP body. @@ -36,6 +67,17 @@ public interface ModifiableHttpMessage extends HttpMessage { * is added to the message. * * @param body Body to set. + * @return this message. + */ + T setBody(DataStream body); + + /** + * Set the body of the message. + * + * @param publisher Body to set. + * @return the builder. */ - void setBody(DataStream body); + default T setBody(Flow.Publisher publisher) { + return setBody(DataStream.ofPublisher(publisher, null, -1)); + } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequest.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequest.java index 306fcf9c6..af5a7b3f6 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequest.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequest.java @@ -11,28 +11,31 @@ /** * A modifiable HTTP request. */ -public interface ModifiableHttpRequest extends ModifiableHttpMessage, HttpRequest { +public interface ModifiableHttpRequest extends ModifiableHttpMessage, HttpRequest { /** * Set the request method. * * @param method Method to set. + * @return this request. */ - void setMethod(String method); + ModifiableHttpRequest setMethod(String method); /** * Set the request URI. * * @param uri SmithyUri to set. + * @return this request. */ - void setUri(SmithyUri uri); + ModifiableHttpRequest setUri(SmithyUri uri); /** * Set the request URI. * * @param uri URI to set. + * @return this request. */ - default void setUri(URI uri) { - setUri(SmithyUri.of(uri)); + default ModifiableHttpRequest setUri(URI uri) { + return setUri(SmithyUri.of(uri)); } @Override diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequestImpl.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequestImpl.java index d8979dcf7..ddcaf8289 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequestImpl.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpRequestImpl.java @@ -33,8 +33,9 @@ public String method() { } @Override - public void setMethod(String method) { + public ModifiableHttpRequest setMethod(String method) { this.method = Objects.requireNonNull(method); + return this; } @Override @@ -43,8 +44,9 @@ public SmithyUri uri() { } @Override - public void setUri(SmithyUri uri) { + public ModifiableHttpRequest setUri(SmithyUri uri) { this.uri = Objects.requireNonNull(uri); + return this; } @Override @@ -53,8 +55,9 @@ public HttpVersion httpVersion() { } @Override - public void setHttpVersion(HttpVersion httpVersion) { + public ModifiableHttpRequest setHttpVersion(HttpVersion httpVersion) { this.httpVersion = Objects.requireNonNull(httpVersion); + return this; } @Override @@ -63,8 +66,9 @@ public ModifiableHttpHeaders headers() { } @Override - public void setHeaders(ModifiableHttpHeaders headers) { + public ModifiableHttpRequest setHeaders(ModifiableHttpHeaders headers) { this.headers = Objects.requireNonNull(headers); + return this; } @Override @@ -73,13 +77,14 @@ public DataStream body() { } @Override - public void setBody(DataStream body) { + public ModifiableHttpRequest setBody(DataStream body) { if (body == null) { this.body = DataStream.ofEmpty(); } else { this.body = body; ModifiableHttpResponseImpl.addBodyHeaders(body, headers); } + return this; } @Override diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponse.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponse.java index 7e97ed31b..1b2c900a9 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponse.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponse.java @@ -8,13 +8,14 @@ /** * A modifiable HTTP response. */ -public interface ModifiableHttpResponse extends ModifiableHttpMessage, HttpResponse { +public interface ModifiableHttpResponse extends ModifiableHttpMessage, HttpResponse { /** * Set the status code. * * @param statusCode Status code to set. + * @return this response. */ - void setStatusCode(int statusCode); + ModifiableHttpResponse setStatusCode(int statusCode); @Override default ModifiableHttpResponse toModifiable() { diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java index 895a8e238..6fdbeca04 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java @@ -25,8 +25,9 @@ final class ModifiableHttpResponseImpl implements ModifiableHttpResponse { } @Override - public void setStatusCode(int statusCode) { + public ModifiableHttpResponse setStatusCode(int statusCode) { this.statusCode = statusCode; + return this; } @Override @@ -40,8 +41,9 @@ public HttpVersion httpVersion() { } @Override - public void setHttpVersion(HttpVersion httpVersion) { + public ModifiableHttpResponse setHttpVersion(HttpVersion httpVersion) { this.httpVersion = Objects.requireNonNull(httpVersion); + return this; } @Override @@ -50,8 +52,9 @@ public ModifiableHttpHeaders headers() { } @Override - public void setHeaders(ModifiableHttpHeaders headers) { + public ModifiableHttpResponse setHeaders(ModifiableHttpHeaders headers) { this.headers = Objects.requireNonNull(headers); + return this; } @Override @@ -60,13 +63,14 @@ public DataStream body() { } @Override - public void setBody(DataStream body) { + public ModifiableHttpResponse setBody(DataStream body) { if (body == null) { this.body = DataStream.ofEmpty(); } else { this.body = body; addBodyHeaders(body, headers); } + return this; } // Shared helper method with ModifiableHttpRequestImpl to set headers based on the provided body. diff --git a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java index 5877dfd2d..55ead62b4 100644 --- a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java +++ b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java @@ -22,11 +22,10 @@ public void canAddHeadersToImmutableHeaders() throws Exception { .headers(HttpHeaders.of(Map.of("foo", List.of("bar")))) .build(); - var builder = r.toBuilder(); - builder.withAddedHeader("foo", "bar2"); - builder.withAddedHeaders(HttpHeaders.of(Map.of("foo", List.of("bar3")))); - var updated = builder.build(); + var mod = r.toModifiableCopy(); + mod.headers().addHeader("foo", "bar2"); + mod.headers().addHeaders(HttpHeaders.of(Map.of("foo", List.of("bar3")))); - assertThat(updated.headers().allValues("foo"), contains("bar", "bar2", "bar3")); + assertThat(mod.headers().allValues("foo"), contains("bar", "bar2", "bar3")); } } From fbbb18f06eeb8554d5b13d044db0178c8ff4f4c2 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Tue, 31 Mar 2026 22:32:37 -0500 Subject: [PATCH 2/4] Remove request/response builders The builders just delegated to the mutable request/response anyway and added unnecessary overhead. Mutable messages are now fluent, have chaining methods like toModifiable and toUnmodifiable, so it obviates the need for builders entirely. --- .../auth/scheme/sigv4/SigV4SignerTrials.java | 14 +- .../client/auth/scheme/sigv4/SigV4Signer.java | 2 +- .../auth/scheme/sigv4/SigV4TestRunner.java | 14 +- .../aws/client/awsjson/AwsJsonProtocol.java | 20 +-- .../awsquery/AwsQueryClientProtocol.java | 11 +- .../aws/client/http/AmzSdkRequestPlugin.java | 4 +- .../client/http/RecursionDetectionPlugin.java | 8 +- .../client/http/AmzSdkRequestPluginTest.java | 16 +- .../http/RecursionDetectionPluginTest.java | 5 +- .../server/restjson/AwsRestJson1Protocol.java | 11 +- .../java/client/core/ClientPipelineTest.java | 24 +-- .../smithy/java/client/core/ClientTest.java | 6 +- .../InjectIdempotencyTokenPluginTest.java | 10 +- .../HttpBindingErrorDeserializerTest.java | 26 +-- .../client/http/JavaHttpClientTransport.java | 12 +- .../http/auth/HttpApiKeyAuthSigner.java | 4 +- .../client/http/auth/HttpBasicAuthSigner.java | 2 +- .../http/auth/HttpBearerAuthSigner.java | 2 +- .../plugins/RequestCompressionPlugin.java | 2 +- .../http/AmznErrorHeaderExtractorTest.java | 60 +++---- .../client/http/HttpClientProtocolTest.java | 8 +- .../http/HttpErrorDeserializerTest.java | 44 ++--- .../http/auth/HttpApiKeyAuthSignerTest.java | 10 +- .../http/auth/HttpBasicAuthSignerTest.java | 22 +-- .../http/auth/HttpBearerAuthSignerTest.java | 22 +-- .../plugins/ApplyHttpRetryInfoPluginTest.java | 26 +-- .../http/plugins/HttpChecksumPluginTest.java | 22 +-- .../plugins/RequestCompressionPluginTest.java | 92 +++++------ .../http/plugins/UserAgentPluginTest.java | 6 +- .../otel/OperationMetricsPluginTest.java | 24 +-- .../java/client/http/mock/MockPlugin.java | 10 +- .../java/client/http/mock/MockPluginTest.java | 2 +- .../java/client/rpcv2/RpcV2CborProtocol.java | 18 +- .../java/dynamicclient/DynamicClientTest.java | 20 +-- .../java/example/dynamodb/DynamoDBSerde.java | 10 +- .../smithy/java/ClientPipelineBench.java | 10 +- .../java/http/api/ArrayHttpHeaders.java | 4 +- .../smithy/java/http/api/HeaderNames.java | 14 +- .../smithy/java/http/api/HttpHeaders.java | 10 ++ .../smithy/java/http/api/HttpMessage.java | 115 ------------- .../smithy/java/http/api/HttpRequest.java | 51 +----- .../smithy/java/http/api/HttpRequestImpl.java | 82 --------- .../smithy/java/http/api/HttpResponse.java | 32 +--- .../java/http/api/HttpResponseImpl.java | 66 -------- .../java/http/api/ModifiableHttpHeaders.java | 10 +- .../java/http/api/ModifiableHttpMessage.java | 72 +++++++- .../java/http/api/SmithyHttpMessageTest.java | 10 +- .../http/api/SmithyHttpRequestImplTest.java | 156 +++++++++--------- .../http/api/SmithyHttpResponseImplTest.java | 136 +++++++-------- .../java/http/binding/RequestSerializer.java | 12 +- .../java/http/binding/ResponseSerializer.java | 10 +- .../smithy/java/mcp/server/HttpMcpProxy.java | 18 +- ...HttpClientRequestProtocolTestProvider.java | 6 +- ...ttpClientResponseProtocolTestProvider.java | 16 +- ...HttpServerRequestProtocolTestProvider.java | 14 +- ...ttpServerResponseProtocolTestProvider.java | 14 +- .../harness/ServerTestClient.java | 10 +- .../java/server/netty/NettyHttpHeaders.java | 4 +- 58 files changed, 609 insertions(+), 852 deletions(-) diff --git a/aws/aws-sigv4/src/jmh/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4SignerTrials.java b/aws/aws-sigv4/src/jmh/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4SignerTrials.java index 2703f8b7a..1940cec0e 100644 --- a/aws/aws-sigv4/src/jmh/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4SignerTrials.java +++ b/aws/aws-sigv4/src/jmh/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4SignerTrials.java @@ -128,12 +128,12 @@ private static HttpRequest parsePostRequest( queryParameters.forEach(queryBuilder::add); uriString += "?" + queryBuilder; } - return HttpRequest.builder() - .method("POST") - .httpVersion(HttpVersion.HTTP_1_1) - .uri(SmithyUri.of(uriString)) - .headers(httpHeaders) - .body(body != null ? DataStream.ofBytes(body.getBytes(StandardCharsets.UTF_8)) : null) - .build(); + return HttpRequest.create() + .setMethod("POST") + .setHttpVersion(HttpVersion.HTTP_1_1) + .setUri(SmithyUri.of(uriString)) + .setHeaders(httpHeaders) + .setBody(body != null ? DataStream.ofBytes(body.getBytes(StandardCharsets.UTF_8)) : null) + .toUnmodifiable(); } } diff --git a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java index f04ddc451..d0b344371 100644 --- a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java +++ b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java @@ -91,7 +91,7 @@ public SignResult sign(HttpRequest request, AwsCredentialsIdentity identity.sessionToken(), !request.body().hasKnownLength()); var signedHeaders = signatureAndSignedHeaders.right; - var mod = request.toModifiableCopy(); + var mod = request.toModifiable(); mod.setHeaders(HttpHeaders.of(signedHeaders).toModifiable()); return new SignResult<>(mod, signatureAndSignedHeaders.left); } diff --git a/aws/aws-sigv4/src/test/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4TestRunner.java b/aws/aws-sigv4/src/test/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4TestRunner.java index 628b6af72..fc5803294 100644 --- a/aws/aws-sigv4/src/test/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4TestRunner.java +++ b/aws/aws-sigv4/src/test/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4TestRunner.java @@ -147,13 +147,13 @@ private static HttpRequest parseRequest(String fileName) { } } - return HttpRequest.builder() - .method(method) - .httpVersion(HttpVersion.HTTP_1_1) - .uri(URI.create("http://" + Objects.requireNonNull(hostValue) + path)) - .headers(HttpHeaders.of(headers)) - .body(body != null ? DataStream.ofBytes(body.toString().getBytes()) : null) - .build(); + return HttpRequest.create() + .setMethod(method) + .setHttpVersion(HttpVersion.HTTP_1_1) + .setUri(URI.create("http://" + Objects.requireNonNull(hostValue) + path)) + .setHeaders(HttpHeaders.of(headers)) + .setBody(body != null ? DataStream.ofBytes(body.toString().getBytes()) : null) + .toUnmodifiable(); } Result createResult( diff --git a/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java b/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java index e8e0621ee..61da97d3b 100644 --- a/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java +++ b/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java @@ -78,22 +78,22 @@ public HttpRequest SmithyUri endpoint ) { var target = service.getName() + "." + operation.schema().id().getName(); - var builder = HttpRequest.builder(); - builder.method("POST"); - builder.uri(endpoint); + var builder = HttpRequest.create(); + builder.setMethod("POST"); + builder.setUri(endpoint); if (operation.inputEventBuilderSupplier() != null) { // Event streaming var encoderFactory = getEventEncoderFactory(operation); var body = RpcEventStreamsUtil.bodyForEventStreaming(encoderFactory, input); - builder.withAddedHeader("x-amz-target", target) - .withAddedHeader("content-type", "application/vnd.amazon.eventstream") - .withAddedHeader("accept", contentType()) - .body(body); + builder.addHeader("x-amz-target", target) + .addHeader("content-type", "application/vnd.amazon.eventstream") + .addHeader("accept", contentType()) + .setBody(body); } else { - builder.withAddedHeader("x-amz-target", target) - .withAddedHeader("content-type", contentType()); + builder.addHeader("x-amz-target", target) + .addHeader("content-type", contentType()); } - return builder.body(DataStream.ofByteBuffer(codec.serialize(input), contentType())).build(); + return builder.setBody(DataStream.ofByteBuffer(codec.serialize(input), contentType())); } @Override diff --git a/aws/client/aws-client-awsquery/src/main/java/software/amazon/smithy/java/aws/client/awsquery/AwsQueryClientProtocol.java b/aws/client/aws-client-awsquery/src/main/java/software/amazon/smithy/java/aws/client/awsquery/AwsQueryClientProtocol.java index 2a7f8d5a3..a8a93a3fa 100644 --- a/aws/client/aws-client-awsquery/src/main/java/software/amazon/smithy/java/aws/client/awsquery/AwsQueryClientProtocol.java +++ b/aws/client/aws-client-awsquery/src/main/java/software/amazon/smithy/java/aws/client/awsquery/AwsQueryClientProtocol.java @@ -80,12 +80,11 @@ public HttpRequest ByteBuffer body = serializer.finish(); - return HttpRequest.builder() - .method("POST") - .uri(endpoint) - .headers(CONTENT_TYPE_HEADERS) - .body(DataStream.ofByteBuffer(body, CONTENT_TYPE)) - .build(); + return HttpRequest.create() + .setMethod("POST") + .setUri(endpoint) + .setHeaders(CONTENT_TYPE_HEADERS) + .setBody(DataStream.ofByteBuffer(body, CONTENT_TYPE)); } @Override diff --git a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java index d6750e15b..ad9c642c7 100644 --- a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java +++ b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPlugin.java @@ -10,6 +10,7 @@ import software.amazon.smithy.java.client.core.ClientPlugin; import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; import software.amazon.smithy.java.client.core.interceptors.RequestHook; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; /** @@ -37,7 +38,8 @@ public RequestT modifyBeforeSigning(RequestHook hook) value.append("; max=").append(max); } return hook.asRequestType( - req.toModifiableCopy().setHeader("amz-sdk-request", value.toString())); + req.toModifiable() + .setHeader(HeaderNames.AMZ_SDK_REQUEST, value.toString())); } } return hook.request(); diff --git a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java index 1b02e064a..2c12d42b3 100644 --- a/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java +++ b/aws/client/aws-client-http/src/main/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPlugin.java @@ -10,6 +10,7 @@ import software.amazon.smithy.java.client.core.ClientPlugin; import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; import software.amazon.smithy.java.client.core.interceptors.RequestHook; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; /** @@ -47,10 +48,9 @@ private record Interceptor(List traceIdHeader) implements ClientIntercep @Override public RequestT modifyBeforeTransmit(RequestHook hook) { if (hook.request() instanceof HttpRequest req) { - if (!req.headers().hasHeader("x-amzn-trace-id")) { - var mod = req.toModifiableCopy(); - mod.headers().setHeader("x-amzn-trace-id", traceIdHeader); - return hook.asRequestType(mod); + if (!req.headers().hasHeader(HeaderNames.X_AMZN_TRACE_ID)) { + return hook.asRequestType( + req.toModifiable().setHeader(HeaderNames.X_AMZN_TRACE_ID, traceIdHeader)); } } return hook.request(); diff --git a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPluginTest.java b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPluginTest.java index 4091a9db8..e1e8df4bf 100644 --- a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPluginTest.java +++ b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/AmzSdkRequestPluginTest.java @@ -74,15 +74,15 @@ public Builder toBuilder() { .build(); mockQueue.enqueue( - HttpResponse.builder() - .statusCode(429) - .body(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) - .build()); + HttpResponse.create() + .setStatusCode(429) + .setBody(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) + .toUnmodifiable()); mockQueue.enqueue( - HttpResponse.builder() - .statusCode(200) - .body(DataStream.ofString("{\"id\":\"1\"}")) - .build()); + HttpResponse.create() + .setStatusCode(200) + .setBody(DataStream.ofString("{\"id\":\"1\"}")) + .toUnmodifiable()); client.call("CreateSprocket"); diff --git a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java index f7ca9aa59..4665f59dc 100644 --- a/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java +++ b/aws/client/aws-client-http/src/test/java/software/amazon/smithy/java/aws/client/http/RecursionDetectionPluginTest.java @@ -48,7 +48,10 @@ private HttpHeaders getSentHeaders(ClientPlugin recursionDetectionPlugin) { private HttpHeaders getSentHeaders(ClientPlugin recursionDetectionPlugin, ClientInterceptor interceptor) { var mockQueue = new MockQueue(); - mockQueue.enqueue(HttpResponse.builder().statusCode(200).body(DataStream.ofString("{\"id\":\"1\"}")).build()); + mockQueue.enqueue(HttpResponse.create() + .setStatusCode(200) + .setBody(DataStream.ofString("{\"id\":\"1\"}")) + .toUnmodifiable()); var mock = MockPlugin.builder().addQueue(mockQueue).build(); var builder = DynamicClient.builder() diff --git a/aws/server/aws-server-restjson/src/main/java/software/amazon/smithy/java/aws/server/restjson/AwsRestJson1Protocol.java b/aws/server/aws-server-restjson/src/main/java/software/amazon/smithy/java/aws/server/restjson/AwsRestJson1Protocol.java index c1ad29137..4eebc1e39 100644 --- a/aws/server/aws-server-restjson/src/main/java/software/amazon/smithy/java/aws/server/restjson/AwsRestJson1Protocol.java +++ b/aws/server/aws-server-restjson/src/main/java/software/amazon/smithy/java/aws/server/restjson/AwsRestJson1Protocol.java @@ -138,12 +138,11 @@ public CompletableFuture deserializeInput(Job job) { .inputShapeBuilder(inputShapeBuilder) .pathLabelValues(labelValues) .request( - HttpRequest.builder() - .headers(headers) - .uri(httpJob.request().uri()) - .method(httpJob.request().method()) - .body(job.request().getDataStream()) - .build()) + HttpRequest.create() + .setHeaders(headers) + .setUri(httpJob.request().uri()) + .setMethod(httpJob.request().method()) + .setBody(job.request().getDataStream())) .payloadCodec(codec) .payloadMediaType("application/json"); diff --git a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientPipelineTest.java b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientPipelineTest.java index 0b058895f..0738f38c7 100644 --- a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientPipelineTest.java +++ b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientPipelineTest.java @@ -137,15 +137,15 @@ public void canRetryRequests() { var mockQueue = new MockQueue() .enqueue( - HttpResponse.builder() - .statusCode(429) - .body(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) - .build()) + HttpResponse.create() + .setStatusCode(429) + .setBody(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) + .toUnmodifiable()) .enqueue( - HttpResponse.builder() - .statusCode(200) - .body(DataStream.ofString("{\"id\":\"1\"}")) - .build()); + HttpResponse.create() + .setStatusCode(200) + .setBody(DataStream.ofString("{\"id\":\"1\"}")) + .toUnmodifiable()); var mock = MockPlugin.builder().addQueue(mockQueue).build(); var client = DynamicClient.builder() @@ -228,10 +228,10 @@ public void endpointAuthSchemeOverridesAugmentSignerProperties() { .build(); var mockQueue = new MockQueue() - .enqueue(HttpResponse.builder() - .statusCode(200) - .body(DataStream.ofString("{\"id\":\"1\"}")) - .build()); + .enqueue(HttpResponse.create() + .setStatusCode(200) + .setBody(DataStream.ofString("{\"id\":\"1\"}")) + .toUnmodifiable()); var mock = MockPlugin.builder().addQueue(mockQueue).build(); var client = DynamicClient.builder() diff --git a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientTest.java b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientTest.java index 2460b2ce1..ebcd68ae6 100644 --- a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientTest.java +++ b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/ClientTest.java @@ -207,7 +207,7 @@ public void correctlyWrapsTransportExceptions() throws URISyntaxException { @Test public void allowsInterceptorRequestOverrides() throws URISyntaxException { var queue = new MockQueue(); - queue.enqueue(HttpResponse.builder().statusCode(200).build()); + queue.enqueue(HttpResponse.create().setStatusCode(200).toUnmodifiable()); var id = "abc"; DynamicClient c = DynamicClient.builder() @@ -239,7 +239,7 @@ public void readBeforeExecution(InputHook hook) { @Test public void requestOverridesPerCallTakePrecedence() throws URISyntaxException { var queue = new MockQueue(); - queue.enqueue(HttpResponse.builder().statusCode(200).build()); + queue.enqueue(HttpResponse.create().setStatusCode(200).toUnmodifiable()); var id = "abc"; DynamicClient c = DynamicClient.builder() @@ -281,7 +281,7 @@ public void readBeforeExecution(InputHook hook) { @Test public void setsCustomEndpoint() { var queue = new MockQueue(); - queue.enqueue(HttpResponse.builder().statusCode(200).build()); + queue.enqueue(HttpResponse.create().setStatusCode(200).toUnmodifiable()); DynamicClient c = DynamicClient.builder() .model(MODEL) diff --git a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPluginTest.java b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPluginTest.java index 39caa1e6e..751b8ee2b 100644 --- a/client/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPluginTest.java +++ b/client/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPluginTest.java @@ -82,11 +82,11 @@ private String callAndGetToken(String operation, Document input) { var mock = MockPlugin.builder() .addMatcher( request -> new MockedResult.Response( - HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("content-type", List.of("application/json")))) - .body(DataStream.ofString("{}")) - .build())) + HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("content-type", List.of("application/json")))) + .setBody(DataStream.ofString("{}")) + .toUnmodifiable())) .build(); var client = DynamicClient.builder() diff --git a/client/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java b/client/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java index 257f606fa..94a8c59df 100644 --- a/client/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java +++ b/client/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java @@ -97,10 +97,10 @@ public void deserializesErrorsWithHttpBindingsToo() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .body(DataStream.ofString("{\"__type\": \"com.foo#Baz\"}")); - var response = responseBuilder.build(); + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setBody(DataStream.ofString("{\"__type\": \"com.foo#Baz\"}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(Baz.class)); @@ -117,10 +117,10 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .body(DataStream.ofString("{\"__type\": \"com.foo#SomeUnknownError\"}")); - var response = responseBuilder.build(); + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setBody(DataStream.ofString("{\"__type\": \"com.foo#SomeUnknownError\"}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(CallException.class)); @@ -139,17 +139,17 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .headers( + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "content-length", List.of("2"), "x-amzn-errortype", List.of("com.foo#SomeUnknownError")))) - .body(DataStream.ofString("{}")); - var response = responseBuilder.build(); + .setBody(DataStream.ofString("{}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(CallException.class)); diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/JavaHttpClientTransport.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/JavaHttpClientTransport.java index 3c8d55a8c..b6a2eecfb 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/JavaHttpClientTransport.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/JavaHttpClientTransport.java @@ -199,12 +199,12 @@ HttpResponse createSmithyResponse(java.net.http.HttpResponse respon var contentType = headers.contentType(); var body = DataStream.ofInputStream(response.body(), contentType, adaptedLength); - return HttpResponse.builder() - .httpVersion(javaToSmithyVersion(response.version())) - .statusCode(response.statusCode()) - .headers(headers) - .body(body) - .build(); + return HttpResponse.create() + .setHttpVersion(javaToSmithyVersion(response.version())) + .setStatusCode(response.statusCode()) + .setHeaders(headers) + .setBody(body) + .toUnmodifiable(); } private static HttpClient.Version smithyToHttpVersion(HttpVersion version) { diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java index 0b3fbf80b..374f56697 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSigner.java @@ -29,7 +29,7 @@ public SignResult sign(HttpRequest request, ApiKeyIdentity identity if (schemeValue != null) { value = schemeValue + " " + value; } - var mod = request.toModifiableCopy(); + var mod = request.toModifiable(); if (mod.headers().hasHeader(name)) { LOGGER.debug("Replaced header value for {}", name); } @@ -42,7 +42,7 @@ public SignResult sign(HttpRequest request, ApiKeyIdentity identity var existingQuery = request.uri().getQuery(); addExistingQueryParams(stringBuilder, existingQuery, name); queryBuilder.write(stringBuilder); - var mod = request.toModifiableCopy(); + var mod = request.toModifiable(); mod.setUri(request.uri().withQuery(stringBuilder.toString())); yield new SignResult<>(mod); } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java index 84e132efa..e065f466d 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java @@ -26,7 +26,7 @@ private HttpBasicAuthSigner() {} public SignResult sign(HttpRequest request, LoginIdentity identity, Context properties) { var identityString = identity.username() + ":" + identity.password(); var base64Value = Base64.getEncoder().encodeToString(identityString.getBytes(StandardCharsets.UTF_8)); - var mod = request.toModifiableCopy(); + var mod = request.toModifiable(); if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { LOGGER.debug("Replaced existing Authorization header value."); } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java index bf280a968..05477be17 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java @@ -22,7 +22,7 @@ private HttpBearerAuthSigner() {} @Override public SignResult sign(HttpRequest request, TokenIdentity identity, Context properties) { - var mod = request.toModifiableCopy(); + var mod = request.toModifiable(); if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { LOGGER.debug("Replaced existing Authorization header value."); } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java index 5b3506efe..6782c70dd 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java @@ -56,7 +56,7 @@ public RequestT modifyBeforeRetryLoop(RequestHook hoo if (algorithmId.equals(algorithm.algorithmId())) { var compressed = algorithm.compress(req.body()); return hook.asRequestType( - req.toModifiableCopy() + req.toModifiable() .setBody(compressed) .addHeader(CONTENT_ENCODING_HEADER, algorithmId)); } diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java index 3974c7976..06b8652c0 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java @@ -27,11 +27,11 @@ public class AmznErrorHeaderExtractorTest { public void checksForHeaderPresence() { var extractor = new AmznErrorHeaderExtractor(); - var response1 = HttpResponse.builder() - .statusCode(400) - .headers(HttpHeaders.of(Map.of("x-amzn-errortype", List.of("foo")))) - .build(); - var response2 = HttpResponse.builder().statusCode(400).build(); + var response1 = HttpResponse.create() + .setStatusCode(400) + .setHeaders(HttpHeaders.of(Map.of("x-amzn-errortype", List.of("foo")))) + .toUnmodifiable(); + var response2 = HttpResponse.create().setStatusCode(400).toUnmodifiable(); assertThat(extractor.hasHeader(response1), is(true)); assertThat(extractor.hasHeader(response2), is(false)); @@ -40,7 +40,7 @@ public void checksForHeaderPresence() { @Test public void resolveIdReturnsNullWhenMissing() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder().statusCode(400).build(); + var response = HttpResponse.create().setStatusCode(400).toUnmodifiable(); assertThat(extractor.resolveId(response, "com.foo", TypeRegistry.builder().build()), nullValue()); } @@ -48,10 +48,10 @@ public void resolveIdReturnsNullWhenMissing() { @Test public void resolvesAbsoluteShapeIds() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers(HttpHeaders.of(Map.of("x-amzn-errortype", List.of("baz#Bam")))) - .build(); + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders(HttpHeaders.of(Map.of("x-amzn-errortype", List.of("baz#Bam")))) + .toUnmodifiable(); var registry = TypeRegistry.builder() .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); @@ -62,14 +62,14 @@ public void resolvesAbsoluteShapeIds() { @Test public void resolvesAbsoluteShapeIdsWithColons() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers( + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "x-amzn-errortype", List.of("baz#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) - .build(); + .toUnmodifiable(); var registry = TypeRegistry.builder() .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); @@ -80,14 +80,14 @@ public void resolvesAbsoluteShapeIdsWithColons() { @Test public void resolvesRelativeShapeIds() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers( + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "x-amzn-errortype", List.of("Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) - .build(); + .toUnmodifiable(); var registry = TypeRegistry.builder() .putType(ShapeId.from("com.foo#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); @@ -98,14 +98,14 @@ public void resolvesRelativeShapeIds() { @Test public void resolvesRelativeShapeIdsWithColons() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers( + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "x-amzn-errortype", List.of("baz#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) - .build(); + .toUnmodifiable(); var registry = TypeRegistry.builder() .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); @@ -116,14 +116,14 @@ public void resolvesRelativeShapeIdsWithColons() { @Test public void resolvesToServiceErrorWhenAbsoluteNotFound() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers( + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "x-amzn-errortype", List.of("other#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) - .build(); + .toUnmodifiable(); var registry = TypeRegistry.builder() .putType(ShapeId.from("com.foo#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); @@ -134,14 +134,14 @@ public void resolvesToServiceErrorWhenAbsoluteNotFound() { @Test public void returnsNullWhenNoTypeFound() { var extractor = new AmznErrorHeaderExtractor(); - var response = HttpResponse.builder() - .statusCode(400) - .headers( + var response = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "x-amzn-errortype", List.of("other#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) - .build(); + .toUnmodifiable(); var registry = TypeRegistry.empty(); assertThat(extractor.resolveId(response, "com.foo", registry), nullValue()); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpClientProtocolTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpClientProtocolTest.java index f4734cf05..5600cd7d7 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpClientProtocolTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpClientProtocolTest.java @@ -53,10 +53,10 @@ O extends SerializableStruct> O deserializeResponse( }; var endpoint = Endpoint.builder().uri("https://example.com/foo%20/bar").build(); - var request = HttpRequest.builder() - .method("GET") - .uri(SmithyUri.of(null, null, -1, "/bam%20", null)) - .build(); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(SmithyUri.of(null, null, -1, "/bam%20", null)) + .toUnmodifiable(); var merged = hcp.setServiceEndpoint(request, endpoint); // It concats the endpoints and maintains percent encoding. diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java index 38b06bbfa..5ef9d9c91 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java @@ -102,14 +102,14 @@ public void createErrorFromHints(int status, String payload, String message) { .serviceId(SERVICE) .build(); var registry = TypeRegistry.empty(); - var responseBuilder = HttpResponse.builder().statusCode(status); + var responseBuilder = HttpResponse.create().setStatusCode(status); if (payload != null) { - responseBuilder.body(DataStream.ofString(payload)); - responseBuilder.headers( + responseBuilder.setBody(DataStream.ofString(payload)); + responseBuilder.setHeaders( HttpHeaders.of(Map.of("content-length", List.of(Integer.toString(payload.length()))))); } - var response = responseBuilder.build(); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result.getMessage(), containsString(message)); @@ -141,17 +141,17 @@ public void deserializesIntoErrorBasedOnHeaders() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .headers( + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "content-length", List.of("2"), "x-amzn-errortype", List.of(Baz.SCHEMA.id().toString())))) - .body(DataStream.ofString("{}")); - var response = responseBuilder.build(); + .setBody(DataStream.ofString("{}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(Baz.class)); @@ -167,10 +167,10 @@ public void deserializesUsingDocumentViaPayloadWithNoContentLength() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .body(DataStream.ofString("{\"__type\": \"com.foo#Baz\"}")); - var response = responseBuilder.build(); + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setBody(DataStream.ofString("{\"__type\": \"com.foo#Baz\"}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(Baz.class)); @@ -186,10 +186,10 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .body(DataStream.ofString("{\"__type\": \"com.foo#SomeUnknownError\"}")); - var response = responseBuilder.build(); + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setBody(DataStream.ofString("{\"__type\": \"com.foo#SomeUnknownError\"}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(CallException.class)); @@ -207,17 +207,17 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() { var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) .build(); - var responseBuilder = HttpResponse.builder() - .statusCode(400) - .headers( + var responseBuilder = HttpResponse.create() + .setStatusCode(400) + .setHeaders( HttpHeaders.of( Map.of( "content-length", List.of("2"), "x-amzn-errortype", List.of("com.foo#SomeUnknownError")))) - .body(DataStream.ofString("{}")); - var response = responseBuilder.build(); + .setBody(DataStream.ofString("{}")); + var response = responseBuilder.toUnmodifiable(); var result = deserializer.createError(Context.create(), OPERATION, registry, response); assertThat(result, instanceOf(CallException.class)); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java index b3e0971c9..ff409d77c 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpApiKeyAuthSignerTest.java @@ -19,11 +19,11 @@ public class HttpApiKeyAuthSignerTest { private static final String API_KEY = "my-api-key"; private static final ApiKeyIdentity TEST_IDENTITY = ApiKeyIdentity.create(API_KEY); - private static final HttpRequest TEST_REQUEST = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .method("PUT") - .uri(SmithyUri.of("https://www.example.com")) - .build(); + private static final HttpRequest TEST_REQUEST = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod("PUT") + .setUri(SmithyUri.of("https://www.example.com")) + .toUnmodifiable(); @Test void testApiKeyAuthSignerAddsHeaderNoScheme() { diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSignerTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSignerTest.java index a5ff5d508..15552cfdb 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSignerTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSignerTest.java @@ -25,11 +25,11 @@ void testBasicAuthSigner() { var username = "username"; var password = "password"; var testIdentity = LoginIdentity.create(username, password); - var request = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .method("PUT") - .uri(URI.create("https://www.example.com")) - .build(); + var request = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod("PUT") + .setUri(URI.create("https://www.example.com")) + .toUnmodifiable(); var expectedHeader = "Basic " + Base64.getEncoder() .encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); @@ -43,12 +43,12 @@ void overwritesExistingHeader() { var username = "username"; var password = "password"; var testIdentity = LoginIdentity.create(username, password); - var request = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .method("PUT") - .headers(HttpHeaders.of(Map.of("Authorization", List.of("FOO", "BAR")))) - .uri(URI.create("https://www.example.com")) - .build(); + var request = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod("PUT") + .setHeaders(HttpHeaders.of(Map.of("Authorization", List.of("FOO", "BAR")))) + .setUri(URI.create("https://www.example.com")) + .toUnmodifiable(); var expectedHeader = "Basic " + Base64.getEncoder() .encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSignerTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSignerTest.java index 7201fa909..f5fc27b5c 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSignerTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSignerTest.java @@ -21,11 +21,11 @@ public class HttpBearerAuthSignerTest { @Test void testBearerAuthSigner() { var tokenIdentity = TokenIdentity.create("token"); - var request = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .method("PUT") - .uri(URI.create("https://www.example.com")) - .build(); + var request = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod("PUT") + .setUri(URI.create("https://www.example.com")) + .toUnmodifiable(); var signedRequest = HttpBearerAuthSigner.INSTANCE.sign(request, tokenIdentity, Context.empty()).signedRequest(); var authHeader = signedRequest.headers().firstValue("authorization"); @@ -35,12 +35,12 @@ void testBearerAuthSigner() { @Test void overwritesExistingHeader() { var tokenIdentity = TokenIdentity.create("token"); - var request = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .method("PUT") - .headers(HttpHeaders.of(Map.of("Authorization", List.of("FOO", "BAR")))) - .uri(URI.create("https://www.example.com")) - .build(); + var request = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod("PUT") + .setHeaders(HttpHeaders.of(Map.of("Authorization", List.of("FOO", "BAR")))) + .setUri(URI.create("https://www.example.com")) + .toUnmodifiable(); var signedRequest = HttpBearerAuthSigner.INSTANCE.sign(request, tokenIdentity, Context.empty()); var authHeader = signedRequest.signedRequest().headers().firstValue("authorization"); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java index 2305f9cdb..f3aa6cff9 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java @@ -28,10 +28,10 @@ public class ApplyHttpRetryInfoPluginTest { @Test public void appliesRetryAfterHeader() { - var response = HttpResponse.builder() - .statusCode(500) - .headers(HttpHeaders.of(Map.of("retry-after", List.of("10")))) - .build(); + var response = HttpResponse.create() + .setStatusCode(500) + .setHeaders(HttpHeaders.of(Map.of("retry-after", List.of("10")))) + .toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); @@ -44,10 +44,10 @@ public void appliesRetryAfterHeader() { @Test public void appliesRetryAfterHeaderDate() { - var response = HttpResponse.builder() - .statusCode(500) - .headers(HttpHeaders.of(Map.of("retry-after", List.of("Wed, 21 Oct 2015 07:28:00 GMT")))) - .build(); + var response = HttpResponse.create() + .setStatusCode(500) + .setHeaders(HttpHeaders.of(Map.of("retry-after", List.of("Wed, 21 Oct 2015 07:28:00 GMT")))) + .toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); context.put(ClockSetting.CLOCK, Clock.fixed(Instant.parse("2015-10-21T05:28:00Z"), ZoneId.of("UTC"))); @@ -61,7 +61,7 @@ public void appliesRetryAfterHeaderDate() { @Test public void appliesThrottlingStatusCode503() { - var response = HttpResponse.builder().statusCode(503).build(); + var response = HttpResponse.create().setStatusCode(503).toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); @@ -74,7 +74,7 @@ public void appliesThrottlingStatusCode503() { @Test public void appliesThrottlingStatusCode429() { - var response = HttpResponse.builder().statusCode(429).build(); + var response = HttpResponse.create().setStatusCode(429).toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); @@ -87,7 +87,7 @@ public void appliesThrottlingStatusCode429() { @Test public void retriesSafe5xx() { - var response = HttpResponse.builder().statusCode(500).build(); + var response = HttpResponse.create().setStatusCode(500).toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); context.put(CallContext.IDEMPOTENCY_TOKEN, "foo"); @@ -101,7 +101,7 @@ public void retriesSafe5xx() { @Test public void doesNotRetryUnsafe5xx() { - var response = HttpResponse.builder().statusCode(500).build(); + var response = HttpResponse.create().setStatusCode(500).toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); @@ -114,7 +114,7 @@ public void doesNotRetryUnsafe5xx() { @Test public void doesNotRetryNormal4xx() { - var response = HttpResponse.builder().statusCode(400).build(); + var response = HttpResponse.create().setStatusCode(400).toUnmodifiable(); var e = new CallException("err"); var context = Context.create(); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPluginTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPluginTest.java index 48c208f71..af82f36c1 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPluginTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPluginTest.java @@ -20,11 +20,11 @@ public class HttpChecksumPluginTest { @Test public void interceptorAddsContentMd5HeaderForKnownBody() throws Exception { var interceptor = new HttpChecksumPlugin.HttpChecksumInterceptor(); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofBytes("test body".getBytes(StandardCharsets.UTF_8))) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofBytes("test body".getBytes(StandardCharsets.UTF_8))) + .toUnmodifiable(); var result = interceptor.addContentMd5Header(req); @@ -36,12 +36,12 @@ public void interceptorAddsContentMd5HeaderForKnownBody() throws Exception { @Test public void interceptorReplacesExistingContentMd5Header() throws Exception { var interceptor = new HttpChecksumPlugin.HttpChecksumInterceptor(); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofBytes("test body".getBytes(StandardCharsets.UTF_8))) - .withAddedHeader("Content-MD5", "wrong-hash") - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofBytes("test body".getBytes(StandardCharsets.UTF_8))) + .addHeader("Content-MD5", "wrong-hash") + .toUnmodifiable(); var result = interceptor.addContentMd5Header(req); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPluginTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPluginTest.java index ce2b25c5d..1ccd67913 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPluginTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPluginTest.java @@ -44,11 +44,11 @@ public void doesNotCompressWhenDisabled() throws Exception { var context = Context.create(); context.put(HttpContext.DISABLE_REQUEST_COMPRESSION, true); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 1); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(REQUEST_BODY)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(REQUEST_BODY)) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req)); @@ -62,11 +62,11 @@ public void compressesWhenBodyMeetsMinSize() throws Exception { var context = Context.create(); String largeBody = REQUEST_BODY.repeat(10); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 10); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(largeBody)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(largeBody)) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req)); @@ -81,11 +81,11 @@ public void doesNotCompressWhenBodyBelowMinSize() throws Exception { var interceptor = new RequestCompressionPlugin.RequestCompressionInterceptor(); var context = Context.create(); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 10000); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(REQUEST_BODY)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(REQUEST_BODY)) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req)); @@ -98,11 +98,11 @@ public void usesDefaultMinCompressionSize() throws Exception { var interceptor = new RequestCompressionPlugin.RequestCompressionInterceptor(); var context = Context.create(); // Body is smaller than default 10240 - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(REQUEST_BODY)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(REQUEST_BODY)) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req)); @@ -117,11 +117,11 @@ public void alwaysCompressesStreamingWithoutKnownLength() throws Exception { context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 999999); var original = "small"; var streamBody = DataStream.ofInputStream(new ByteArrayInputStream(original.getBytes(StandardCharsets.UTF_8))); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(streamBody) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(streamBody) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithStreamingInput(), context, new TestInput(), req)); @@ -137,11 +137,11 @@ public void throwsForNegativeMinCompressionSize() throws Exception { var context = Context.create(); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, -1); var largeBody = REQUEST_BODY.repeat(100); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(largeBody)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(largeBody)) + .toUnmodifiable(); var hook = new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req); Assertions.assertThrows(IllegalArgumentException.class, () -> interceptor.modifyBeforeRetryLoop(hook)); @@ -153,11 +153,11 @@ public void throwsForMinCompressionSizeExceedingCap() throws Exception { var context = Context.create(); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 10485761); var largeBody = REQUEST_BODY.repeat(100); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(largeBody)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(largeBody)) + .toUnmodifiable(); var hook = new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req); Assertions.assertThrows(IllegalArgumentException.class, () -> interceptor.modifyBeforeRetryLoop(hook)); @@ -168,11 +168,11 @@ public void doesNotCompressWhenTraitAbsent() throws Exception { var interceptor = new RequestCompressionPlugin.RequestCompressionInterceptor(); var context = Context.create(); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 1); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(REQUEST_BODY)) - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(REQUEST_BODY)) + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithoutCompressionTrait(), context, new TestInput(), req)); @@ -185,12 +185,12 @@ public void appendsGzipToExistingContentEncoding() throws Exception { var interceptor = new RequestCompressionPlugin.RequestCompressionInterceptor(); var context = Context.create(); context.put(HttpContext.REQUEST_MIN_COMPRESSION_SIZE_BYTES, 10); - var req = HttpRequest.builder() - .uri(new URI("/")) - .method("POST") - .body(DataStream.ofString(REQUEST_BODY)) - .withAddedHeader("Content-Encoding", "custom") - .build(); + var req = HttpRequest.create() + .setUri(new URI("/")) + .setMethod("POST") + .setBody(DataStream.ofString(REQUEST_BODY)) + .addHeader("Content-Encoding", "custom") + .toUnmodifiable(); var result = interceptor.modifyBeforeRetryLoop( new RequestHook<>(createOperationWithCompressionTrait(), context, new TestInput(), req)); diff --git a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/UserAgentPluginTest.java b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/UserAgentPluginTest.java index ee764b5d7..9ce8619b4 100644 --- a/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/UserAgentPluginTest.java +++ b/client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/UserAgentPluginTest.java @@ -35,7 +35,7 @@ public class UserAgentPluginTest { public void addsDefaultAgent() throws Exception { UserAgentPlugin.UserAgentInterceptor interceptor = new UserAgentPlugin.UserAgentInterceptor(); var context = Context.create(); - var req = HttpRequest.builder().uri(new URI("/")).method("GET").build(); + var req = HttpRequest.create().setUri(new URI("/")).setMethod("GET").toUnmodifiable(); var foo = new Foo(); var updated = interceptor.modifyBeforeSigning(new RequestHook<>(createOperation(), context, foo, req)); @@ -51,7 +51,7 @@ public void addsApplicationId() throws Exception { UserAgentPlugin.UserAgentInterceptor interceptor = new UserAgentPlugin.UserAgentInterceptor(); var context = Context.create(); context.put(ClientContext.APPLICATION_ID, "hello there"); - var req = HttpRequest.builder().uri(new URI("/")).method("GET").build(); + var req = HttpRequest.create().setUri(new URI("/")).setMethod("GET").toUnmodifiable(); var foo = new Foo(); var updated = interceptor.modifyBeforeSigning(new RequestHook<>(createOperation(), context, foo, req)); @@ -83,7 +83,7 @@ public void addsFeatureIds() throws Exception { s.add(Features.BAZ); context.put(CallContext.FEATURE_IDS, s); - var req = HttpRequest.builder().uri(new URI("/")).method("GET").build(); + var req = HttpRequest.create().setUri(new URI("/")).setMethod("GET").toUnmodifiable(); var foo = new Foo(); var updated = interceptor.modifyBeforeSigning(new RequestHook<>(createOperation(), context, foo, req)); diff --git a/client/client-metrics-otel/src/test/java/software/amazon/smithy/java/client/metrics/otel/OperationMetricsPluginTest.java b/client/client-metrics-otel/src/test/java/software/amazon/smithy/java/client/metrics/otel/OperationMetricsPluginTest.java index e0f29f35e..91b027e74 100644 --- a/client/client-metrics-otel/src/test/java/software/amazon/smithy/java/client/metrics/otel/OperationMetricsPluginTest.java +++ b/client/client-metrics-otel/src/test/java/software/amazon/smithy/java/client/metrics/otel/OperationMetricsPluginTest.java @@ -107,10 +107,10 @@ void setUp() { public void recordsTheExpectedMetrics() throws URISyntaxException { // Arrange var queue = new MockQueue(); - queue.enqueue(HttpResponse.builder() - .body(DataStream.ofString("{\"id\":\"10\"}")) - .statusCode(200) - .build()); + queue.enqueue(HttpResponse.create() + .setBody(DataStream.ofString("{\"id\":\"10\"}")) + .setStatusCode(200) + .toUnmodifiable()); var client = createClient(queue); // Act @@ -134,14 +134,14 @@ public void recordsTheExpectedMetrics() throws URISyntaxException { public void recordsRetryAttempts() throws URISyntaxException { // Arrange var queue = new MockQueue(); - queue.enqueue(HttpResponse.builder() - .body(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) - .statusCode(429) - .build()); - queue.enqueue(HttpResponse.builder() - .body(DataStream.ofString("{\"id\":\"10\"}")) - .statusCode(200) - .build()); + queue.enqueue(HttpResponse.create() + .setBody(DataStream.ofString("{\"__type\":\"InvalidSprocketId\"}")) + .setStatusCode(429) + .toUnmodifiable()); + queue.enqueue(HttpResponse.create() + .setBody(DataStream.ofString("{\"id\":\"10\"}")) + .setStatusCode(200) + .toUnmodifiable()); var client = createClient(queue); // Act diff --git a/client/client-mock-plugin/src/main/java/software/amazon/smithy/java/client/http/mock/MockPlugin.java b/client/client-mock-plugin/src/main/java/software/amazon/smithy/java/client/http/mock/MockPlugin.java index 3895f334b..c2a1cba34 100644 --- a/client/client-mock-plugin/src/main/java/software/amazon/smithy/java/client/http/mock/MockPlugin.java +++ b/client/client-mock-plugin/src/main/java/software/amazon/smithy/java/client/http/mock/MockPlugin.java @@ -313,11 +313,11 @@ private HttpResponse replyWithMockOutput(CurrentRequest currentRequest, MockedRe protocol.serializeOutput(job, output.output()).join(); } - return HttpResponse.builder() - .statusCode(response.getStatusCode()) - .headers(response.headers()) - .body(response.getSerializedValue()) - .build(); + return HttpResponse.create() + .setStatusCode(response.getStatusCode()) + .setHeaders(response.headers()) + .setBody(response.getSerializedValue()) + .toUnmodifiable(); } private static ServerProtocol detectServerProtocol(ShapeId id) { diff --git a/client/client-mock-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java b/client/client-mock-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java index 1d62a99ca..e44ba4a39 100644 --- a/client/client-mock-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java +++ b/client/client-mock-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java @@ -145,7 +145,7 @@ public void returnsMockedHttpResponses() throws URISyntaxException { AtomicReference ref = new AtomicReference<>(); var mockQueue = new MockQueue() - .enqueue(HttpResponse.builder().statusCode(404).withAddedHeader("a", "b").build()); + .enqueue(HttpResponse.create().setStatusCode(404).addHeader("a", "b").toUnmodifiable()); var mock = MockPlugin.builder().addQueue(mockQueue).build(); var client = DynamicClient.builder() diff --git a/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java b/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java index f8065f900..e3afe5559 100644 --- a/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java +++ b/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java @@ -75,25 +75,25 @@ public HttpRequest SmithyUri endpoint ) { var target = "/service/" + service.getName() + "/operation/" + operation.schema().id().getName(); - var builder = HttpRequest.builder().method("POST").uri(endpoint.withConcatPath(target)); + var builder = HttpRequest.create().setMethod("POST").setUri(endpoint.withConcatPath(target)); - builder.httpVersion(HttpVersion.HTTP_2); + builder.setHttpVersion(HttpVersion.HTTP_2); if (operation.inputSchema().hasTrait(TraitKey.UNIT_TYPE_TRAIT)) { // Top-level Unit types do not get serialized - builder.headers(HttpHeaders.of(headersForEmptyBody())) - .body(DataStream.ofEmpty()); + builder.setHeaders(HttpHeaders.of(headersForEmptyBody())) + .setBody(DataStream.ofEmpty()); } else if (operation.inputEventBuilderSupplier() != null) { // Event streaming var encoderFactory = getEventEncoderFactory(operation); var body = RpcEventStreamsUtil.bodyForEventStreaming(encoderFactory, input); - builder.headers(HttpHeaders.of(headersForEventStreaming())) - .body(body); + builder.setHeaders(HttpHeaders.of(headersForEventStreaming())) + .setBody(body); } else { // Regular request - builder.headers(HttpHeaders.of(headers())) - .body(getBody(input)); + builder.setHeaders(HttpHeaders.of(headers())) + .setBody(getBody(input)); } - return builder.build(); + return builder.toUnmodifiable(); } @Override diff --git a/client/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java b/client/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java index 98641d3a6..813f17a5b 100644 --- a/client/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java +++ b/client/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java @@ -181,11 +181,11 @@ public MessageExchange messageExchange() { @Override public HttpResponse send(Context context, HttpRequest request) { - return HttpResponse.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .statusCode(200) - .body(DataStream.ofString("{\"id\":\"1\"}")) - .build(); + return HttpResponse.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setStatusCode(200) + .setBody(DataStream.ofString("{\"id\":\"1\"}")) + .toUnmodifiable(); } }; } @@ -285,11 +285,11 @@ public MessageExchange messageExchange() { @Override public HttpResponse send(Context context, HttpRequest request) { - return HttpResponse.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .statusCode(400) - .body(DataStream.ofString(payload)) - .build(); + return HttpResponse.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setStatusCode(400) + .setBody(DataStream.ofString(payload)) + .toUnmodifiable(); } }; } diff --git a/examples/dynamodb-client/src/jmh/java/software/amazon/smithy/java/example/dynamodb/DynamoDBSerde.java b/examples/dynamodb-client/src/jmh/java/software/amazon/smithy/java/example/dynamodb/DynamoDBSerde.java index 8d1c70570..77054e32e 100644 --- a/examples/dynamodb-client/src/jmh/java/software/amazon/smithy/java/example/dynamodb/DynamoDBSerde.java +++ b/examples/dynamodb-client/src/jmh/java/software/amazon/smithy/java/example/dynamodb/DynamoDBSerde.java @@ -95,7 +95,7 @@ public static class GetItemState { public void setup() throws URISyntaxException { // This isn't actually used, but needed for the protocol implementation. endpoint = new URI("https://dynamodb.us-east-1.amazonaws.com"); - req = HttpRequest.builder().method("POST").uri(endpoint).build(); + req = HttpRequest.create().setMethod("POST").setUri(endpoint).toUnmodifiable(); operation = GetItem.instance(); protocol = new AwsJson1Protocol(ShapeId.from("com.amazonaws.dynamodb#DynamoDB_20120810")); } @@ -144,10 +144,10 @@ private static byte[] toUtf8ByteArray(Map item) { } private HttpResponse fullResponse(byte[] itemBytes) { - return HttpResponse.builder() - .statusCode(200) - .body(DataStream.ofBytes(itemBytes)) - .build(); + return HttpResponse.create() + .setStatusCode(200) + .setBody(DataStream.ofBytes(itemBytes)) + .toUnmodifiable(); } static final class ItemFactory { diff --git a/examples/restjson-client/src/jmh/java/software/amazon/smithy/java/ClientPipelineBench.java b/examples/restjson-client/src/jmh/java/software/amazon/smithy/java/ClientPipelineBench.java index 45c22d525..2c7ba29ce 100644 --- a/examples/restjson-client/src/jmh/java/software/amazon/smithy/java/ClientPipelineBench.java +++ b/examples/restjson-client/src/jmh/java/software/amazon/smithy/java/ClientPipelineBench.java @@ -48,11 +48,11 @@ public class ClientPipelineBench { @Setup public void setup() { - var mockResponse = HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("content-type", List.of("application/json")))) - .body(DataStream.ofBytes(RESPONSE_BODY, "application/json")) - .build(); + var mockResponse = HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("content-type", List.of("application/json")))) + .setBody(DataStream.ofBytes(RESPONSE_BODY, "application/json")) + .toUnmodifiable(); var mockPlugin = MockPlugin.builder() .trackRequests(false) diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ArrayHttpHeaders.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ArrayHttpHeaders.java index cc621f3a5..f2f77f280 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ArrayHttpHeaders.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ArrayHttpHeaders.java @@ -183,7 +183,7 @@ public void addHeaders(HttpHeaders headers) { } @Override - public void setHeaders(HttpHeaders headers) { + public void placeHeaders(HttpHeaders headers) { if (headers instanceof AbstractArrayHttpHeaders ah) { // Single-pass compaction: remove all entries whose names appear in source int write = 0; @@ -208,7 +208,7 @@ public void setHeaders(HttpHeaders headers) { System.arraycopy(ah.array, 0, array, size * 2, ah.size * 2); size += ah.size; } else { - ModifiableHttpHeaders.super.setHeaders(headers); + ModifiableHttpHeaders.super.placeHeaders(headers); } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java index 044d289d4..eb287c559 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java @@ -79,7 +79,9 @@ private HeaderNames() {} public static final String WWW_AUTHENTICATE = "www-authenticate"; // === Amazon-specific Headers === + public static final String AMZ_SDK_REQUEST = "amz-sdk-request"; public static final String X_AMZN_REQUESTID = "x-amzn-requestid"; + public static final String X_AMZN_TRACE_ID = "x-amzn-trace-id"; public static final String X_AMZ_REQUEST_ID = "x-amz-request-id"; // Direct-indexed by header name length. GROUPS[len] is the candidate array for that length, or null. @@ -113,7 +115,7 @@ private HeaderNames() {} LAST_MODIFIED }; GROUPS[14] = new String[] {CONTENT_LENGTH, ACCEPT_CHARSET}; - GROUPS[15] = new String[] {ACCEPT_ENCODING, ACCEPT_LANGUAGE}; + GROUPS[15] = new String[] {ACCEPT_ENCODING, ACCEPT_LANGUAGE, AMZ_SDK_REQUEST, X_AMZN_TRACE_ID}; GROUPS[16] = new String[] { CONTENT_ENCODING, CONTENT_LANGUAGE, @@ -528,6 +530,16 @@ private static String matchBucket15(String name) { return ACCEPT_LANGUAGE; } break; + case 'r': // amz-sdk-[r]equest + if (name.regionMatches(true, 0, AMZ_SDK_REQUEST, 0, 15)) { + return AMZ_SDK_REQUEST; + } + break; + case 't': // x-amzn-[t]race-id + if (name.regionMatches(true, 0, X_AMZN_TRACE_ID, 0, 15)) { + return X_AMZN_TRACE_ID; + } + break; } return HeaderUtils.normalizeName(name); } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpHeaders.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpHeaders.java index 025ee5990..22436c697 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpHeaders.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpHeaders.java @@ -44,6 +44,16 @@ static ModifiableHttpHeaders ofModifiable(int expectedPairs) { return new ArrayHttpHeaders(expectedPairs); } + /** + * Create mutable HttpHeaders. + * + * @param headers Headers to set. + * @return the created headers. + */ + static ModifiableHttpHeaders ofModifiable(Map> headers) { + return of(headers).toModifiable(); + } + /** * Check if the given header is present. * diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpMessage.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpMessage.java index 0463cad5f..892bd51c9 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpMessage.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpMessage.java @@ -5,10 +5,6 @@ package software.amazon.smithy.java.http.api; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Flow; import software.amazon.smithy.java.io.datastream.DataStream; /** @@ -85,115 +81,4 @@ default void close() { body.close(); } } - - /** - * Builder for HTTP messages. - * - * @param Builder type. - */ - @SuppressWarnings("rawtypes") - interface Builder { - /** - * Set the HTTP version. - * - * @param httpVersion HTTP version of the message. - * @return the builder. - */ - B httpVersion(HttpVersion httpVersion); - - /** - * Set the body of the message. - * - * @param publisher Body to set. - * @return the builder. - */ - default B body(Flow.Publisher publisher) { - return body(DataStream.ofPublisher(publisher, null, -1)); - } - - /** - * Set the body of the message. - * - * @param body Body to set. - * @return the builder. - */ - B body(DataStream body); - - /** - * Set the headers of the message, replacing any previously set headers. - * - * @param headers Headers to set. - * @return the builder. - */ - B headers(HttpHeaders headers); - - /** - * Add and merge in the given headers to the message. - * - * @param name Header name to append. - * @param value Header value to append. - * @return the builder. - */ - B withAddedHeader(String name, String value); - - /** - * Add and merge in the given headers to the message. - * - * @param headers Headers to add and merge in. - * @return the builder. - */ - B withAddedHeaders(Map> headers); - - /** - * Add and merge in the given headers to the message. - * - * @param headers Headers to add and merge in. - * @return the builder. - */ - @SuppressWarnings("unchecked") - default B withAddedHeaders(HttpHeaders headers) { - headers.forEachEntry(this::withAddedHeader); - return (B) this; - } - - /** - * Put the given headers onto the message, replacing any existing headers with the same names. - * - * @param headers Headers to put. - * @return the builder. - */ - B withReplacedHeaders(Map> headers); - - /** - * Put the given headers onto the message, replacing any existing headers with the same names. - * - * @param headers Headers to put. - * @return the builder. - */ - @SuppressWarnings("unchecked") - default B withReplacedHeaders(HttpHeaders headers) { - headers.forEachEntry(this::withReplacedHeader); - return (B) this; - } - - /** - * Replaces a header. - * - * @param name Header name to replace. - * @param value Header value to replace. - * @return the builder. - */ - B withReplacedHeader(String name, String value); - - /** - * Replaces a header. - * - * @param name Header name to replace. - * @param values Header values to replace. - * @return the builder. - */ - default B withReplacedHeader(String name, List values) { - return withReplacedHeaders(Map.of(name, values)); - } - } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java index 7107cb134..38d9caf76 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequest.java @@ -5,7 +5,6 @@ package software.amazon.smithy.java.http.api; -import java.net.URI; import software.amazon.smithy.java.io.uri.SmithyUri; /** @@ -52,53 +51,7 @@ public interface HttpRequest extends HttpMessage { * * @return the created builder. */ - static Builder builder() { - return new HttpRequestImpl.Builder(); - } - - /** - * HTTP request message builder. - */ - interface Builder extends HttpMessage.Builder { - /** - * Create the request. - * - * @return the created request. - * @throws NullPointerException if method or uri are missing. - */ - HttpRequest build(); - - /** - * Build a modifiable HTTP request. - * - * @return the mutable HTTP request. - */ - ModifiableHttpRequest buildModifiable(); - - /** - * Set the HTTP method. - * - * @param method Method to set. - * @return the builder. - */ - Builder method(String method); - - /** - * Set the URI of the message. - * - * @param uri SmithyUri to set. - * @return the builder. - */ - Builder uri(SmithyUri uri); - - /** - * Set the URI of the message from a {@link URI}. - * - * @param uri URI to set. - * @return the builder. - */ - default Builder uri(URI uri) { - return uri(SmithyUri.of(uri)); - } + static ModifiableHttpRequest create() { + return new ModifiableHttpRequestImpl(); } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequestImpl.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequestImpl.java index dc673abf3..8d0cba16c 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequestImpl.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpRequestImpl.java @@ -5,11 +5,6 @@ package software.amazon.smithy.java.http.api; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Flow; import software.amazon.smithy.java.io.datastream.DataStream; import software.amazon.smithy.java.io.uri.SmithyUri; @@ -44,81 +39,4 @@ public ModifiableHttpRequest toModifiableCopy() { mod.setBody(body); return mod; } - - static final class Builder implements HttpRequest.Builder { - private final ModifiableHttpRequest modifiableHttpRequest = new ModifiableHttpRequestImpl(); - - Builder() {} - - public Builder httpVersion(HttpVersion httpVersion) { - modifiableHttpRequest.setHttpVersion(httpVersion); - return this; - } - - public Builder method(String method) { - modifiableHttpRequest.setMethod(method); - return this; - } - - public Builder uri(SmithyUri uri) { - modifiableHttpRequest.setUri(uri); - return this; - } - - public Builder body(Flow.Publisher publisher) { - return body(DataStream.ofPublisher(publisher, null, -1)); - } - - public Builder body(DataStream body) { - modifiableHttpRequest.setBody(body); - return this; - } - - @Override - public HttpRequest.Builder headers(HttpHeaders headers) { - modifiableHttpRequest.setHeaders(headers.toModifiable()); - return this; - } - - @Override - public Builder withAddedHeader(String name, String value) { - modifiableHttpRequest.headers().addHeader(name, value); - return this; - } - - @Override - public Builder withAddedHeaders(Map> headers) { - modifiableHttpRequest.headers().addHeaders(headers); - return this; - } - - @Override - public Builder withReplacedHeaders(Map> headers) { - modifiableHttpRequest.headers().setHeaders(headers); - return this; - } - - @Override - public HttpRequest.Builder withReplacedHeader(String name, String value) { - modifiableHttpRequest.headers().setHeader(name, value); - return this; - } - - private void beforeBuild() { - Objects.requireNonNull(modifiableHttpRequest.method(), "method not set"); - Objects.requireNonNull(modifiableHttpRequest.uri(), "uri not set"); - } - - @Override - public HttpRequest build() { - beforeBuild(); - return modifiableHttpRequest.toUnmodifiable(); - } - - @Override - public ModifiableHttpRequest buildModifiable() { - beforeBuild(); - return modifiableHttpRequest.toModifiableCopy(); - } - } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java index 0fb3d0391..0c0b57d85 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponse.java @@ -42,35 +42,7 @@ public interface HttpResponse extends HttpMessage { * * @return the created builder. */ - static Builder builder() { - return new HttpResponseImpl.Builder(); - } - - /** - * HTTP response message builder. - */ - interface Builder extends HttpMessage.Builder { - /** - * Create the response. - * - * @return the created response. - * @throws NullPointerException if status code is missing. - */ - HttpResponse build(); - - /** - * Build a modifiable HTTP response. - * - * @return the mutable HTTP response. - */ - ModifiableHttpResponse buildModifiable(); - - /** - * Set the status code of the response. - * - * @param statusCode Response status code. - * @return the builder. - */ - Builder statusCode(int statusCode); + static ModifiableHttpResponse create() { + return new ModifiableHttpResponseImpl(); } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponseImpl.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponseImpl.java index 8b1fa320a..ff3de6527 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponseImpl.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HttpResponseImpl.java @@ -5,8 +5,6 @@ package software.amazon.smithy.java.http.api; -import java.util.List; -import java.util.Map; import software.amazon.smithy.java.io.datastream.DataStream; record HttpResponseImpl( @@ -38,68 +36,4 @@ public ModifiableHttpResponse toModifiableCopy() { mod.setBody(body); return mod; } - - static final class Builder implements HttpResponse.Builder { - ModifiableHttpResponseImpl modifiableResponse = new ModifiableHttpResponseImpl(); - - Builder() {} - - @Override - public Builder httpVersion(HttpVersion httpVersion) { - modifiableResponse.setHttpVersion(httpVersion); - return this; - } - - @Override - public Builder statusCode(int statusCode) { - modifiableResponse.setStatusCode(statusCode); - return this; - } - - @Override - public Builder body(DataStream body) { - modifiableResponse.setBody(body); - return this; - } - - @Override - public Builder headers(HttpHeaders headers) { - modifiableResponse.setHeaders(headers.toModifiable()); - return this; - } - - @Override - public Builder withAddedHeader(String name, String value) { - modifiableResponse.headers().addHeader(name, value); - return this; - } - - @Override - public Builder withAddedHeaders(Map> headers) { - modifiableResponse.headers().addHeaders(headers); - return this; - } - - @Override - public Builder withReplacedHeaders(Map> headers) { - modifiableResponse.headers().setHeaders(headers); - return this; - } - - @Override - public HttpResponse.Builder withReplacedHeader(String name, String value) { - modifiableResponse.headers().setHeader(name, value); - return this; - } - - @Override - public HttpResponse build() { - return modifiableResponse.toUnmodifiable(); - } - - @Override - public ModifiableHttpResponse buildModifiable() { - return modifiableResponse.toModifiableCopy(); - } - } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpHeaders.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpHeaders.java index 532923cac..d7b17ad8d 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpHeaders.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpHeaders.java @@ -131,7 +131,7 @@ default List setHeaderIfAbsent(String name, String value) { * * @param headers Map of case-insensitive header names to their values. */ - default void setHeaders(Map> headers) { + default void placeHeaders(Map> headers) { for (var entry : headers.entrySet()) { setHeader(entry.getKey(), entry.getValue()); } @@ -143,10 +143,8 @@ default void setHeaders(Map> headers) { * * @param headers HTTP headers to copy from. */ - default void setHeaders(HttpHeaders headers) { - for (var e : headers.map().entrySet()) { - setHeader(e.getKey(), e.getValue()); - } + default void placeHeaders(HttpHeaders headers) { + headers.forEachEntry(this::setHeader); } /** @@ -171,7 +169,7 @@ default ModifiableHttpHeaders copy() { return ah.copy(); } else { var copy = new ArrayHttpHeaders(size()); - copy.setHeaders(this); + copy.placeHeaders(this); return copy; } } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java index 609f490bb..95be35dc5 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpMessage.java @@ -6,6 +6,8 @@ package software.amazon.smithy.java.http.api; import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; import java.util.concurrent.Flow; import software.amazon.smithy.java.io.datastream.DataStream; @@ -35,6 +37,50 @@ public interface ModifiableHttpMessage> exten */ T setHeaders(ModifiableHttpHeaders headers); + /** + * Set the HTTP headers. + * + * @param headers Headers to set. + * @return this message. + */ + default T setHeaders(HttpHeaders headers) { + return setHeaders(headers.toModifiable()); + } + + /** + * Set the HTTP headers. + * + * @param headers Headers to set. + * @return this message. + */ + default T setHeaders(Map> headers) { + return setHeaders(HttpHeaders.ofModifiable(headers)); + } + + /** + * Puts the given {@code headers}, similarly to if {@link #setHeader(String, List)} were + * to be called for each entry in the given HttpHeaders. + * + * @param headers HTTP headers to copy from. + * @return this message. + */ + default T placeHeaders(HttpHeaders headers) { + headers().placeHeaders(headers); + return (T) this; + } + + /** + * Puts the given {@code headers}, similarly to if {@link #setHeader(String, List)} were + * to be called for each entry in the given HttpHeaders. + * + * @param headers HTTP headers to copy from. + * @return this message. + */ + default T placeHeaders(Map> headers) { + headers().placeHeaders(headers); + return (T) this; + } + /** * Set a specific header by name and replace any existing value. * @@ -47,6 +93,18 @@ default T setHeader(String name, String value) { return (T) this; } + /** + * Set a specific header by name and replace any existing values. + * + * @param name Header to set. + * @param values Values to set. + * @return modifiable message. + */ + default T setHeader(String name, List values) { + headers().setHeader(name, values); + return (T) this; + } + /** * Add a specific header by name to any existing headers with the same name. * @@ -59,6 +117,18 @@ default T addHeader(String name, String value) { return (T) this; } + /** + * Add a specific header by name to any existing headers with the same name. + * + * @param name Header to add. + * @param value Values to add. + * @return modifiable message. + */ + default T addHeader(String name, List value) { + headers().addHeader(name, value); + return (T) this; + } + /** * Set the HTTP body. * @@ -75,7 +145,7 @@ default T addHeader(String name, String value) { * Set the body of the message. * * @param publisher Body to set. - * @return the builder. + * @return this message. */ default T setBody(Flow.Publisher publisher) { return setBody(DataStream.ofPublisher(publisher, null, -1)); diff --git a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java index 55ead62b4..db5f5e588 100644 --- a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java +++ b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpMessageTest.java @@ -16,11 +16,11 @@ public class SmithyHttpMessageTest { @Test public void canAddHeadersToImmutableHeaders() throws Exception { - var r = HttpRequest.builder() - .method("GET") - .uri(new URI("https://example.com")) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar")))) - .build(); + var r = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://example.com")) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar")))) + .toUnmodifiable(); var mod = r.toModifiableCopy(); mod.headers().addHeader("foo", "bar2"); diff --git a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpRequestImplTest.java b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpRequestImplTest.java index 764067a4a..c5155c0fc 100644 --- a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpRequestImplTest.java +++ b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpRequestImplTest.java @@ -21,13 +21,13 @@ public class SmithyHttpRequestImplTest { @Test public void addHeaders() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .withAddedHeader("foo", "bar ") - .withAddedHeader("Baz", "bam") - .withAddedHeader("FOO", "bar2") - .build(); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .addHeader("foo", "bar ") + .addHeader("Baz", "bam") + .addHeader("FOO", "bar2") + .toUnmodifiable(); assertThat(request.headers().allValues("foo"), contains("bar", "bar2")); assertThat(request.headers().allValues("baz"), contains("bam")); @@ -36,14 +36,14 @@ public void addHeaders() throws Exception { @Test public void addHeadersToExistingHeaders() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withAddedHeader("foo", "bar ") - .withAddedHeader("Baz", "bam") - .withAddedHeader("FOO", "bar2") - .build(); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) + .addHeader("foo", "bar ") + .addHeader("Baz", "bam") + .addHeader("FOO", "bar2") + .toUnmodifiable(); assertThat(request.headers().allValues("foo"), contains("bar0", "bar", "bar2")); assertThat(request.headers().allValues("baz"), contains("bam")); @@ -53,62 +53,62 @@ public void addHeadersToExistingHeaders() throws Exception { @Test public void replacesHeaders() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .build(); - - assertThat(request.headers().allValues("foo"), contains("bar")); - assertThat(request.headers().allValues("baz"), contains("bam")); - assertThat(request.headers().allValues("bam"), contains("A")); - assertThat(request.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam")); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))); + request.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = request.toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam")); } @Test public void replacesHeadersOnExisting() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withAddedHeader("a", "b") - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .build(); - - assertThat(request.headers().allValues("foo"), contains("bar")); - assertThat(request.headers().allValues("baz"), contains("bam")); - assertThat(request.headers().allValues("bam"), contains("A")); - assertThat(request.headers().allValues("a"), contains("b")); - assertThat(request.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) + .addHeader("a", "b"); + request.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = request.toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().allValues("a"), contains("b")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); } @Test public void addsHeadersToReplacements() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .withAddedHeader("a", "b") - .withAddedHeader("foo", "bar2") - .build(); - - assertThat(request.headers().allValues("foo"), contains("bar", "bar2")); - assertThat(request.headers().allValues("baz"), contains("bam")); - assertThat(request.headers().allValues("bam"), contains("A")); - assertThat(request.headers().allValues("a"), contains("b")); - assertThat(request.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))); + request.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = request.addHeader("a", "b") + .addHeader("foo", "bar2") + .toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar", "bar2")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().allValues("a"), contains("b")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); } @Test public void bodyAutoAddsContentTypeAndLength() throws Exception { var body = DataStream.ofString("hello", "text/plain"); - var request = HttpRequest.builder() - .method("POST") - .uri(new URI("https://localhost")) - .body(body) - .build(); + var request = HttpRequest.create() + .setMethod("POST") + .setUri(new URI("https://localhost")) + .setBody(body) + .toUnmodifiable(); assertEquals("5", request.headers().firstValue("content-length")); assertEquals("text/plain", request.headers().firstValue("content-type")); @@ -117,12 +117,12 @@ public void bodyAutoAddsContentTypeAndLength() throws Exception { @Test public void bodyHeadersCanBeOverridden() throws Exception { var body = DataStream.ofString("hello"); - var request = HttpRequest.builder() - .method("POST") - .uri(new URI("https://localhost")) - .body(body) - .withReplacedHeaders(Map.of("content-type", List.of("application/json"))) - .build(); + var request = HttpRequest.create() + .setMethod("POST") + .setUri(new URI("https://localhost")) + .setBody(body) + .setHeader("content-type", "application/json") + .toUnmodifiable(); assertEquals("5", request.headers().firstValue("content-length")); assertEquals("application/json", request.headers().firstValue("content-type")); @@ -130,21 +130,21 @@ public void bodyHeadersCanBeOverridden() throws Exception { @Test public void toUnmodifiableReturnsImmutable() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .build(); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .toUnmodifiable(); assertSame(request, request.toUnmodifiable()); } @Test public void toModifiableReturnsCopy() throws Exception { - var request = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .withAddedHeader("foo", "bar") - .build(); + var request = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .addHeader("foo", "bar") + .toUnmodifiable(); var modifiable = request.toModifiable(); modifiable.headers().addHeader("foo", "baz"); @@ -155,11 +155,11 @@ public void toModifiableReturnsCopy() throws Exception { @Test public void modifiableCopyIsIndependent() throws Exception { - var modifiable = HttpRequest.builder() - .method("GET") - .uri(new URI("https://localhost")) - .withAddedHeader("foo", "bar") - .buildModifiable(); + var modifiable = HttpRequest.create() + .setMethod("GET") + .setUri(new URI("https://localhost")) + .addHeader("foo", "bar") + .toModifiable(); var copy = modifiable.toModifiableCopy(); copy.headers().addHeader("foo", "baz"); diff --git a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpResponseImplTest.java b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpResponseImplTest.java index aebcf6b23..a45f0417b 100644 --- a/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpResponseImplTest.java +++ b/http/http-api/src/test/java/software/amazon/smithy/java/http/api/SmithyHttpResponseImplTest.java @@ -20,12 +20,12 @@ public class SmithyHttpResponseImplTest { @Test public void addHeaders() { - var response = HttpResponse.builder() - .statusCode(200) - .withAddedHeader("foo", "bar ") - .withAddedHeader("Baz", "bam") - .withAddedHeader("FOO", "bar2") - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .addHeader("foo", "bar ") + .addHeader("Baz", "bam") + .addHeader("FOO", "bar2") + .toUnmodifiable(); assertThat(response.headers().allValues("foo"), contains("bar", "bar2")); assertThat(response.headers().allValues("baz"), contains("bam")); @@ -34,13 +34,13 @@ public void addHeaders() { @Test public void addHeadersToExistingHeaders() { - var response = HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withAddedHeader("foo", "bar ") - .withAddedHeader("Baz", "bam") - .withAddedHeader("FOO", "bar2") - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) + .addHeader("foo", "bar ") + .addHeader("Baz", "bam") + .addHeader("FOO", "bar2") + .toUnmodifiable(); assertThat(response.headers().allValues("foo"), contains("bar0", "bar", "bar2")); assertThat(response.headers().allValues("baz"), contains("bam")); @@ -50,58 +50,58 @@ public void addHeadersToExistingHeaders() { @Test public void replacesHeaders() { - var response = HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .build(); - - assertThat(response.headers().allValues("foo"), contains("bar")); - assertThat(response.headers().allValues("baz"), contains("bam")); - assertThat(response.headers().allValues("bam"), contains("A")); - assertThat(response.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam")); + var response = HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))); + response.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = response.toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam")); } @Test public void replacesHeadersOnExisting() { - var response = HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withAddedHeader("a", "b") - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .build(); - - assertThat(response.headers().allValues("foo"), contains("bar")); - assertThat(response.headers().allValues("baz"), contains("bam")); - assertThat(response.headers().allValues("bam"), contains("A")); - assertThat(response.headers().allValues("a"), contains("b")); - assertThat(response.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); + var response = HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) + .addHeader("a", "b"); + response.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = response.toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().allValues("a"), contains("b")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); } @Test public void addsHeadersToReplacements() { - var response = HttpResponse.builder() - .statusCode(200) - .headers(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))) - .withReplacedHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))) - .withAddedHeader("a", "b") - .withAddedHeader("foo", "bar2") - .build(); - - assertThat(response.headers().allValues("foo"), contains("bar", "bar2")); - assertThat(response.headers().allValues("baz"), contains("bam")); - assertThat(response.headers().allValues("bam"), contains("A")); - assertThat(response.headers().allValues("a"), contains("b")); - assertThat(response.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); + var response = HttpResponse.create() + .setStatusCode(200) + .setHeaders(HttpHeaders.of(Map.of("foo", List.of("bar0"), "bam", List.of(" A ")))); + response.headers().placeHeaders(Map.of("foo", List.of("bar "), "Baz", List.of("bam"))); + var result = response.addHeader("a", "b") + .addHeader("foo", "bar2") + .toUnmodifiable(); + + assertThat(result.headers().allValues("foo"), contains("bar", "bar2")); + assertThat(result.headers().allValues("baz"), contains("bam")); + assertThat(result.headers().allValues("bam"), contains("A")); + assertThat(result.headers().allValues("a"), contains("b")); + assertThat(result.headers().map().keySet(), containsInAnyOrder("foo", "baz", "bam", "a")); } @Test public void bodyAutoAddsContentTypeAndLength() { var body = DataStream.ofString("hello", "text/plain"); - var response = HttpResponse.builder() - .statusCode(200) - .body(body) - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .setBody(body) + .toUnmodifiable(); assertEquals("5", response.headers().firstValue("content-length")); assertEquals("text/plain", response.headers().firstValue("content-type")); @@ -110,11 +110,11 @@ public void bodyAutoAddsContentTypeAndLength() { @Test public void bodyHeadersCanBeOverridden() { var body = DataStream.ofString("hello"); - var response = HttpResponse.builder() - .statusCode(200) - .body(body) - .withReplacedHeaders(Map.of("content-type", List.of("application/json"))) - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .setBody(body) + .setHeader("content-type", "application/json") + .toUnmodifiable(); assertEquals("5", response.headers().firstValue("content-length")); assertEquals("application/json", response.headers().firstValue("content-type")); @@ -122,19 +122,19 @@ public void bodyHeadersCanBeOverridden() { @Test public void toUnmodifiableReturnsImmutable() { - var response = HttpResponse.builder() - .statusCode(200) - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .toUnmodifiable(); assertSame(response, response.toUnmodifiable()); } @Test public void toModifiableReturnsCopy() { - var response = HttpResponse.builder() - .statusCode(200) - .withAddedHeader("foo", "bar") - .build(); + var response = HttpResponse.create() + .setStatusCode(200) + .addHeader("foo", "bar") + .toUnmodifiable(); var modifiable = response.toModifiable(); modifiable.headers().addHeader("foo", "baz"); @@ -145,10 +145,10 @@ public void toModifiableReturnsCopy() { @Test public void modifiableCopyIsIndependent() { - var modifiable = HttpResponse.builder() - .statusCode(200) - .withAddedHeader("foo", "bar") - .buildModifiable(); + var modifiable = HttpResponse.create() + .setStatusCode(200) + .addHeader("foo", "bar") + .toModifiable(); var copy = modifiable.toModifiableCopy(); copy.headers().addHeader("foo", "baz"); diff --git a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/RequestSerializer.java b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/RequestSerializer.java index a8f157a25..7d0f2d886 100644 --- a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/RequestSerializer.java +++ b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/RequestSerializer.java @@ -157,21 +157,21 @@ public HttpRequest serializeRequest() { resolvedUri = resolvedUri.withQuery(serializer.getQueryString()); } - HttpRequest.Builder builder = HttpRequest.builder() - .method(httpTrait.getMethod()) - .uri(resolvedUri); + var builder = HttpRequest.create() + .setMethod(httpTrait.getMethod()) + .setUri(resolvedUri); var eventStream = serializer.getEventStream(); if (eventStream != null && operation.inputEventBuilderSupplier() != null) { ProtocolEventStreamWriter> writer = ProtocolEventStreamWriter.of(eventStream); writer.setEventEncoderFactory((EventEncoderFactory) eventStreamEncodingFactory); - builder.body(writer.toDataStream()); + builder.setBody(writer.toDataStream()); serializer.setContentType(eventStreamEncodingFactory.contentType()); } else if (serializer.hasBody()) { - builder.body(serializer.getBody()); + builder.setBody(serializer.getBody()); } - return builder.headers(serializer.getHeaders()).build(); + return builder.setHeaders(serializer.getHeaders()).toUnmodifiable(); } } diff --git a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseSerializer.java b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseSerializer.java index 948ce310a..704cb722d 100644 --- a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseSerializer.java +++ b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseSerializer.java @@ -176,8 +176,8 @@ public HttpResponse serializeResponse() { shapeValue.serialize(serializer); serializer.flush(); - var builder = HttpResponse.builder() - .statusCode(serializer.getResponseStatus()); + var builder = HttpResponse.create() + .setStatusCode(serializer.getResponseStatus()); var eventStream = serializer.getEventStream(); if (eventStream != null && operation.outputEventBuilderSupplier() != null) { @@ -185,12 +185,12 @@ public HttpResponse serializeResponse() { ProtocolEventStreamWriter.of(eventStream); writer.setEventEncoderFactory(eventEncoderFactory); writer.activate(); - builder.body(writer.toDataStream()); + builder.setBody(writer.toDataStream()); serializer.setContentType(eventEncoderFactory.contentType()); } else if (serializer.hasBody()) { - builder.body(serializer.getBody()); + builder.setBody(serializer.getBody()); } - return builder.headers(serializer.getHeaders()).build(); + return builder.setHeaders(serializer.getHeaders()).toUnmodifiable(); } } diff --git a/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java b/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java index ec091b85e..89425cf79 100644 --- a/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java +++ b/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java @@ -108,17 +108,17 @@ public CompletableFuture rpc(JsonRpcRequest request) { String protocolVersionHeader = getProtocolVersion().identifier(); - HttpRequest.Builder requestBuilder = HttpRequest.builder() - .uri(endpoint) - .method("POST") - .withAddedHeader("Content-Type", "application/json") - .withAddedHeader("Accept", "application/json, text/event-stream") - .withAddedHeader("MCP-Protocol-Version", protocolVersionHeader); + var requestBuilder = HttpRequest.create() + .setUri(endpoint) + .setMethod("POST") + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json, text/event-stream") + .addHeader("MCP-Protocol-Version", protocolVersionHeader); // Include session ID if we have one String currentSessionId = sessionId; if (currentSessionId != null) { - requestBuilder.withAddedHeader("Mcp-Session-Id", currentSessionId); + requestBuilder.addHeader("Mcp-Session-Id", currentSessionId); LOG.debug("Including session ID in request: method={}, sessionId={}", request.getMethod(), currentSessionId); @@ -127,8 +127,8 @@ public CompletableFuture rpc(JsonRpcRequest request) { } HttpRequest httpRequest = requestBuilder - .body(DataStream.ofBytes(body, "application/json")) - .build(); + .setBody(DataStream.ofBytes(body, "application/json")) + .toUnmodifiable(); Context context = Context.create(); context.put(HttpContext.HTTP_REQUEST_TIMEOUT, timeout); diff --git a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientRequestProtocolTestProvider.java b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientRequestProtocolTestProvider.java index afeecb39b..3cf4bd6b8 100644 --- a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientRequestProtocolTestProvider.java +++ b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientRequestProtocolTestProvider.java @@ -178,9 +178,9 @@ public Object resolveParameter( } private static final class TestTransport implements ClientTransport { - private static final HttpResponse exceptionalResponse = HttpResponse.builder() - .statusCode(555) - .build(); + private static final HttpResponse exceptionalResponse = HttpResponse.create() + .setStatusCode(555) + .toUnmodifiable(); private HttpRequest capturedRequest; diff --git a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java index ee65e2aa3..e5e685b47 100644 --- a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java +++ b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java @@ -143,9 +143,9 @@ private record TestTransport(HttpResponseTestCase testCase) implements @Override public HttpResponse send(Context context, HttpRequest request) { - var builder = HttpResponse.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .statusCode(testCase.getCode()); + var builder = HttpResponse.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setStatusCode(testCase.getCode()); // Add headers Map> headerMap = new HashMap<>(); @@ -153,23 +153,23 @@ public HttpResponse send(Context context, HttpRequest request) { headerMap.put(headerEntry.getKey(), List.of(headerEntry.getValue())); } testCase.getBodyMediaType().ifPresent(mediaType -> headerMap.put("content-type", List.of(mediaType))); - builder.headers(HttpHeaders.of(headerMap)); + builder.setHeaders(HttpHeaders.of(headerMap)); // Add request body if present; testCase.getBody().ifPresent(body -> { if (testCase.getBodyMediaType().isPresent()) { var type = testCase.getBodyMediaType().get(); if (ProtocolTestProvider.isBinaryMediaType(type)) { - builder.body(DataStream.ofBytes(Base64.getDecoder().decode(body), type)); + builder.setBody(DataStream.ofBytes(Base64.getDecoder().decode(body), type)); } else { - builder.body(DataStream.ofString(body, type)); + builder.setBody(DataStream.ofString(body, type)); } } else { - builder.body(DataStream.ofString(body)); + builder.setBody(DataStream.ofString(body)); } }); - return builder.build(); + return builder.toUnmodifiable(); } @Override diff --git a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerRequestProtocolTestProvider.java b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerRequestProtocolTestProvider.java index 6b937ec58..9e83b4ce9 100644 --- a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerRequestProtocolTestProvider.java +++ b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerRequestProtocolTestProvider.java @@ -66,13 +66,13 @@ protected Stream generateProtocolTests( } else { mapper = String::getBytes; } - var request = HttpRequest.builder() - .uri(createUri) - .body(DataStream.ofBytes(testCase.getBody().map(mapper).orElse(new byte[0]))) - .httpVersion(HttpVersion.HTTP_1_1) - .method(testCase.getMethod()) - .headers(headers) - .build(); + var request = HttpRequest.create() + .setUri(createUri) + .setBody(DataStream.ofBytes(testCase.getBody().map(mapper).orElse(new byte[0]))) + .setHttpVersion(HttpVersion.HTTP_1_1) + .setMethod(testCase.getMethod()) + .setHeaders(headers) + .toUnmodifiable(); invocationContexts.add( new ServerRequestInvocationContext( testCase, diff --git a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerResponseProtocolTestProvider.java b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerResponseProtocolTestProvider.java index 4211fa192..3c1b2b777 100644 --- a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerResponseProtocolTestProvider.java +++ b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpServerResponseProtocolTestProvider.java @@ -65,13 +65,13 @@ protected Stream generateProtocolTests( headers.put("content-length", List.of("0")); headers.put("content-type", List.of("application/json")); - var request = HttpRequest.builder() - .httpVersion(HttpVersion.HTTP_1_1) - .body(DataStream.ofBytes(new byte[0])) - .uri(testData.endpoint()) - .headers(HttpHeaders.of(headers)) - .method("POST") - .build(); + var request = HttpRequest.create() + .setHttpVersion(HttpVersion.HTTP_1_1) + .setBody(DataStream.ofBytes(new byte[0])) + .setUri(testData.endpoint()) + .setHeaders(HttpHeaders.of(headers)) + .setMethod("POST") + .toUnmodifiable(); invocationContexts.add( new ServerResponseInvocationContext( testCase, diff --git a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/ServerTestClient.java b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/ServerTestClient.java index 7cd289519..7ebcddcb5 100644 --- a/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/ServerTestClient.java +++ b/protocol-test-harness/src/main/java/software/amazon/smithy/java/protocoltests/harness/ServerTestClient.java @@ -55,11 +55,11 @@ HttpResponse sendRequest(HttpRequest request) { var response = httpClient.send( httpRequestBuilder.build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()); - return HttpResponse.builder() - .statusCode(response.statusCode()) - .body(DataStream.ofBytes(response.body())) - .headers(HttpHeaders.of(response.headers().map())) - .build(); + return HttpResponse.create() + .setStatusCode(response.statusCode()) + .setBody(DataStream.ofBytes(response.body())) + .setHeaders(HttpHeaders.of(response.headers().map())) + .toUnmodifiable(); } catch (InterruptedException | IOException e) { throw new RuntimeException(e); diff --git a/server/server-netty/src/main/java/software/amazon/smithy/java/server/netty/NettyHttpHeaders.java b/server/server-netty/src/main/java/software/amazon/smithy/java/server/netty/NettyHttpHeaders.java index a08c55b1d..46c7b5f77 100644 --- a/server/server-netty/src/main/java/software/amazon/smithy/java/server/netty/NettyHttpHeaders.java +++ b/server/server-netty/src/main/java/software/amazon/smithy/java/server/netty/NettyHttpHeaders.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.ModifiableHttpHeaders; final class NettyHttpHeaders implements ModifiableHttpHeaders { @@ -136,7 +137,8 @@ public Set keySet() { @Override public void forEachEntry(BiConsumer consumer) { for (var entry : nettyHeaders) { - consumer.accept(entry.getKey(), entry.getValue()); + // consumers expect canonicalized names + consumer.accept(HeaderNames.canonicalize(entry.getKey()), entry.getValue()); } } From 7c2f3a7e46833d8bc60f60a9fcd8665e7a360c43 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Wed, 1 Apr 2026 10:47:06 -0500 Subject: [PATCH 3/4] Make use of more HeaderNames --- .../client/auth/scheme/sigv4/SigV4Signer.java | 19 ++--- .../aws/client/awsjson/AwsJsonProtocol.java | 11 +-- .../client/http/auth/HttpBasicAuthSigner.java | 6 +- .../http/auth/HttpBearerAuthSigner.java | 6 +- .../http/plugins/HttpChecksumPlugin.java | 3 +- .../plugins/RequestCompressionPlugin.java | 4 +- .../java/client/rpcv2/RpcV2CborProtocol.java | 16 ++-- .../smithy/java/http/api/HeaderNames.java | 74 +++++++++++++++---- .../http/api/ModifiableHttpResponseImpl.java | 4 +- .../smithy/java/mcp/server/HttpMcpProxy.java | 9 ++- .../smithy/java/server/core/CorsHeaders.java | 22 +++--- .../java/server/core/CorsHeadersTest.java | 18 ++--- 12 files changed, 122 insertions(+), 70 deletions(-) diff --git a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java index d0b344371..548afe6a5 100644 --- a/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java +++ b/aws/aws-sigv4/src/main/java/software/amazon/smithy/java/aws/client/auth/scheme/sigv4/SigV4Signer.java @@ -24,6 +24,7 @@ import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.aws.auth.api.identity.AwsCredentialsIdentity; import software.amazon.smithy.java.context.Context; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.io.datastream.DataStream; @@ -39,11 +40,11 @@ final class SigV4Signer implements Signer { private static final InternalLogger LOGGER = InternalLogger.getLogger(SigV4Signer.class); private static final List HEADERS_TO_IGNORE_IN_LOWER_CASE = List.of( - "connection", - "content-length", - "x-amzn-trace-id", - "user-agent", - "expect"); + HeaderNames.CONNECTION, + HeaderNames.CONTENT_LENGTH, + HeaderNames.X_AMZN_TRACE_ID, + HeaderNames.USER_AGENT, + HeaderNames.EXPECT); private static final String ALGORITHM = "AWS4-HMAC-SHA256"; private static final String TERMINATOR = "aws4_request"; @@ -92,7 +93,7 @@ public SignResult sign(HttpRequest request, AwsCredentialsIdentity !request.body().hasKnownLength()); var signedHeaders = signatureAndSignedHeaders.right; var mod = request.toModifiable(); - mod.setHeaders(HttpHeaders.of(signedHeaders).toModifiable()); + mod.setHeaders(signedHeaders); return new SignResult<>(mod, signatureAndSignedHeaders.left); } @@ -124,16 +125,16 @@ private Pair>> createSignedHeaders( // AWS4 requires a number of headers to be set before signing including 'Host' and 'X-Amz-Date' var hostHeader = uriUsingStandardPort(uri) ? uri.getHost() + ':' + uri.getPort() : uri.getHost(); - headers.put("host", List.of(hostHeader)); + headers.put(HeaderNames.HOST, List.of(hostHeader)); var sb = signingResources.sb; var signingDate = signingTimestamp.atOffset(ZoneOffset.UTC).toLocalDateTime(); var dateStamp = formatDate(signingDate, sb); var requestTime = formatRfc3339(signingDate, dateStamp, sb); - headers.put("x-amz-date", List.of(requestTime)); + headers.put(HeaderNames.X_AMZ_DATE, List.of(requestTime)); if (sessionToken != null) { - headers.put("x-amz-security-token", List.of(sessionToken)); + headers.put(HeaderNames.X_AMZ_SECURITY_TOKEN, List.of(sessionToken)); } // Determine sorted list of headers to sign diff --git a/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java b/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java index 61da97d3b..1904a1695 100644 --- a/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java +++ b/aws/client/aws-client-awsjson/src/main/java/software/amazon/smithy/java/aws/client/awsjson/AwsJsonProtocol.java @@ -21,6 +21,7 @@ import software.amazon.smithy.java.core.serde.event.EventDecoderFactory; import software.amazon.smithy.java.core.serde.event.EventEncoderFactory; import software.amazon.smithy.java.core.serde.event.EventStreamingException; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.http.api.HttpResponse; import software.amazon.smithy.java.io.datastream.DataStream; @@ -85,13 +86,13 @@ public HttpRequest // Event streaming var encoderFactory = getEventEncoderFactory(operation); var body = RpcEventStreamsUtil.bodyForEventStreaming(encoderFactory, input); - builder.addHeader("x-amz-target", target) - .addHeader("content-type", "application/vnd.amazon.eventstream") - .addHeader("accept", contentType()) + builder.addHeader(HeaderNames.X_AMZ_TARGET, target) + .addHeader(HeaderNames.CONTENT_TYPE, "application/vnd.amazon.eventstream") + .addHeader(HeaderNames.ACCEPT, contentType()) .setBody(body); } else { - builder.addHeader("x-amz-target", target) - .addHeader("content-type", contentType()); + builder.addHeader(HeaderNames.X_AMZ_TARGET, target) + .addHeader(HeaderNames.CONTENT_TYPE, contentType()); } return builder.setBody(DataStream.ofByteBuffer(codec.serialize(input), contentType())); } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java index e065f466d..f6f778bbe 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBasicAuthSigner.java @@ -11,13 +11,13 @@ import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.auth.api.identity.LoginIdentity; import software.amazon.smithy.java.context.Context; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.logging.InternalLogger; final class HttpBasicAuthSigner implements Signer { static final HttpBasicAuthSigner INSTANCE = new HttpBasicAuthSigner(); private static final InternalLogger LOGGER = InternalLogger.getLogger(HttpBasicAuthSigner.class); - private static final String AUTHORIZATION_HEADER = "authorization"; private static final String SCHEME = "Basic"; private HttpBasicAuthSigner() {} @@ -27,9 +27,9 @@ public SignResult sign(HttpRequest request, LoginIdentity identity, var identityString = identity.username() + ":" + identity.password(); var base64Value = Base64.getEncoder().encodeToString(identityString.getBytes(StandardCharsets.UTF_8)); var mod = request.toModifiable(); - if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { + if (mod.headers().hasHeader(HeaderNames.AUTHORIZATION)) { LOGGER.debug("Replaced existing Authorization header value."); } - return new SignResult<>(mod.setHeader(AUTHORIZATION_HEADER, SCHEME + " " + base64Value)); + return new SignResult<>(mod.setHeader(HeaderNames.AUTHORIZATION, SCHEME + " " + base64Value)); } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java index 05477be17..cb08b80a1 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/auth/HttpBearerAuthSigner.java @@ -9,13 +9,13 @@ import software.amazon.smithy.java.auth.api.Signer; import software.amazon.smithy.java.auth.api.identity.TokenIdentity; import software.amazon.smithy.java.context.Context; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.logging.InternalLogger; final class HttpBearerAuthSigner implements Signer { static final HttpBearerAuthSigner INSTANCE = new HttpBearerAuthSigner(); private static final InternalLogger LOGGER = InternalLogger.getLogger(HttpBearerAuthSigner.class); - private static final String AUTHORIZATION_HEADER = "authorization"; private static final String SCHEME = "Bearer"; private HttpBearerAuthSigner() {} @@ -23,9 +23,9 @@ private HttpBearerAuthSigner() {} @Override public SignResult sign(HttpRequest request, TokenIdentity identity, Context properties) { var mod = request.toModifiable(); - if (mod.headers().hasHeader(AUTHORIZATION_HEADER)) { + if (mod.headers().hasHeader(HeaderNames.AUTHORIZATION)) { LOGGER.debug("Replaced existing Authorization header value."); } - return new SignResult<>(mod.setHeader(AUTHORIZATION_HEADER, SCHEME + " " + identity.token())); + return new SignResult<>(mod.setHeader(HeaderNames.AUTHORIZATION, SCHEME + " " + identity.token())); } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java index aa3a73ec7..b914fa1c1 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/HttpChecksumPlugin.java @@ -14,6 +14,7 @@ import software.amazon.smithy.java.client.core.interceptors.RequestHook; import software.amazon.smithy.java.client.http.HttpMessageExchange; import software.amazon.smithy.java.core.schema.TraitKey; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.io.ByteBufferUtils; import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait; @@ -55,7 +56,7 @@ static HttpRequest addContentMd5Header(HttpRequest request) { byte[] hash = MessageDigest.getInstance("MD5").digest(bytes); String base64Hash = Base64.getEncoder().encodeToString(hash); var modifiable = request.toModifiable(); - modifiable.headers().setHeader("content-md5", base64Hash); + modifiable.headers().setHeader(HeaderNames.CONTENT_MD5, base64Hash); return modifiable; } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("Unable to fetch message digest instance for MD5", e); diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java index 6782c70dd..8fdb7892b 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/RequestCompressionPlugin.java @@ -15,6 +15,7 @@ import software.amazon.smithy.java.client.http.compression.CompressionAlgorithm; import software.amazon.smithy.java.context.Context; import software.amazon.smithy.java.core.schema.TraitKey; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.io.datastream.DataStream; import software.amazon.smithy.model.traits.RequestCompressionTrait; @@ -38,7 +39,6 @@ static final class RequestCompressionInterceptor implements ClientInterceptor { private static final int DEFAULT_MIN_COMPRESSION_SIZE_BYTES = 10240; // This cap matches ApiGateway's spec: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-openapi-minimum-compression-size.html private static final int MIN_COMPRESSION_SIZE_CAP = 10485760; - private static final String CONTENT_ENCODING_HEADER = "Content-Encoding"; private static final ClientInterceptor INSTANCE = new RequestCompressionInterceptor(); private static final TraitKey REQUEST_COMPRESSION_TRAIT_KEY = TraitKey.get(RequestCompressionTrait.class); @@ -58,7 +58,7 @@ public RequestT modifyBeforeRetryLoop(RequestHook hoo return hook.asRequestType( req.toModifiable() .setBody(compressed) - .addHeader(CONTENT_ENCODING_HEADER, algorithmId)); + .addHeader(HeaderNames.CONTENT_ENCODING, algorithmId)); } } } diff --git a/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java b/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java index e3afe5559..292ebd060 100644 --- a/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java +++ b/client/client-rpcv2-cbor/src/main/java/software/amazon/smithy/java/client/rpcv2/RpcV2CborProtocol.java @@ -30,6 +30,7 @@ import software.amazon.smithy.java.core.serde.event.EventDecoderFactory; import software.amazon.smithy.java.core.serde.event.EventEncoderFactory; import software.amazon.smithy.java.core.serde.event.EventStreamingException; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.http.api.HttpResponse; @@ -138,19 +139,24 @@ private DataStream getBody(SerializableStruct input) { } private Map> headers() { - return Map.of("smithy-protocol", SMITHY_PROTOCOL, "Content-Type", CONTENT_TYPE, "Accept", CONTENT_TYPE); + return Map.of(HeaderNames.SMITHY_PROTOCOL, + SMITHY_PROTOCOL, + HeaderNames.CONTENT_TYPE, + CONTENT_TYPE, + HeaderNames.ACCEPT, + CONTENT_TYPE); } private Map> headersForEmptyBody() { - return Map.of("smithy-protocol", SMITHY_PROTOCOL, "Accept", CONTENT_TYPE); + return Map.of(HeaderNames.SMITHY_PROTOCOL, SMITHY_PROTOCOL, HeaderNames.ACCEPT, CONTENT_TYPE); } private Map> headersForEventStreaming() { - return Map.of("smithy-protocol", + return Map.of(HeaderNames.SMITHY_PROTOCOL, SMITHY_PROTOCOL, - "Content-Type", + HeaderNames.CONTENT_TYPE, List.of("application/vnd.amazon.eventstream"), - "Accept", + HeaderNames.ACCEPT, CONTENT_TYPE); } diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java index eb287c559..0c5f7b33c 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java @@ -41,6 +41,7 @@ private HeaderNames() {} public static final String CONTENT_LANGUAGE = "content-language"; public static final String CONTENT_LENGTH = "content-length"; public static final String CONTENT_LOCATION = "content-location"; + public static final String CONTENT_MD5 = "content-md5"; public static final String CONTENT_RANGE = "content-range"; public static final String CONTENT_TYPE = "content-type"; public static final String COOKIE = "cookie"; @@ -60,6 +61,7 @@ private HeaderNames() {} public static final String LINK = "link"; public static final String LOCATION = "location"; public static final String MAX_FORWARDS = "max-forwards"; + public static final String ORIGIN = "origin"; public static final String PROXY_AUTHENTICATE = "proxy-authenticate"; public static final String PROXY_AUTHORIZATION = "proxy-authorization"; public static final String PROXY_CONNECTION = "proxy-connection"; @@ -69,6 +71,7 @@ private HeaderNames() {} public static final String RETRY_AFTER = "retry-after"; public static final String SERVER = "server"; public static final String SET_COOKIE = "set-cookie"; + public static final String SMITHY_PROTOCOL = "smithy-protocol"; public static final String STRICT_TRANSPORT_SECURITY = "strict-transport-security"; public static final String TRAILER = "trailer"; public static final String TRANSFER_ENCODING = "transfer-encoding"; @@ -82,16 +85,19 @@ private HeaderNames() {} public static final String AMZ_SDK_REQUEST = "amz-sdk-request"; public static final String X_AMZN_REQUESTID = "x-amzn-requestid"; public static final String X_AMZN_TRACE_ID = "x-amzn-trace-id"; + public static final String X_AMZ_DATE = "x-amz-date"; public static final String X_AMZ_REQUEST_ID = "x-amz-request-id"; + public static final String X_AMZ_SECURITY_TOKEN = "x-amz-security-token"; + public static final String X_AMZ_TARGET = "x-amz-target"; // Direct-indexed by header name length. GROUPS[len] is the candidate array for that length, or null. private static final String[][] GROUPS = new String[28][]; static { GROUPS[3] = new String[] {AGE, VIA}; - GROUPS[4] = new String[] {DATE, ETAG, FROM, HOST, LINK, VARY}; - GROUPS[5] = new String[] {ALLOW, RANGE, PSEUDO_PATH}; - GROUPS[6] = new String[] {ACCEPT, COOKIE, EXPECT, SERVER}; + GROUPS[4] = new String[] {DATE, HOST, ETAG, FROM, LINK, VARY}; + GROUPS[5] = new String[] {RANGE, ALLOW, PSEUDO_PATH}; + GROUPS[6] = new String[] {ACCEPT, EXPECT, ORIGIN, SERVER, COOKIE}; GROUPS[7] = new String[] { EXPIRES, REFERER, @@ -103,9 +109,9 @@ private HeaderNames() {} PSEUDO_STATUS }; GROUPS[8] = new String[] {IF_MATCH, IF_RANGE, LOCATION}; - GROUPS[10] = new String[] {CONNECTION, KEEP_ALIVE, SET_COOKIE, USER_AGENT, PSEUDO_AUTHORITY}; - GROUPS[11] = new String[] {RETRY_AFTER}; - GROUPS[12] = new String[] {CONTENT_TYPE, MAX_FORWARDS}; + GROUPS[10] = new String[] {CONNECTION, USER_AGENT, KEEP_ALIVE, X_AMZ_DATE, PSEUDO_AUTHORITY, SET_COOKIE}; + GROUPS[11] = new String[] {RETRY_AFTER, CONTENT_MD5}; + GROUPS[12] = new String[] {CONTENT_TYPE, X_AMZ_TARGET, MAX_FORWARDS}; GROUPS[13] = new String[] { ACCEPT_RANGES, AUTHORIZATION, @@ -115,19 +121,20 @@ private HeaderNames() {} LAST_MODIFIED }; GROUPS[14] = new String[] {CONTENT_LENGTH, ACCEPT_CHARSET}; - GROUPS[15] = new String[] {ACCEPT_ENCODING, ACCEPT_LANGUAGE, AMZ_SDK_REQUEST, X_AMZN_TRACE_ID}; + GROUPS[15] = new String[] {SMITHY_PROTOCOL, ACCEPT_ENCODING, ACCEPT_LANGUAGE, AMZ_SDK_REQUEST, X_AMZN_TRACE_ID}; GROUPS[16] = new String[] { + X_AMZN_REQUESTID, + X_AMZ_REQUEST_ID, CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, PROXY_CONNECTION, - WWW_AUTHENTICATE, - X_AMZN_REQUESTID, - X_AMZ_REQUEST_ID + WWW_AUTHENTICATE }; - GROUPS[17] = new String[] {IF_MODIFIED_SINCE, TRANSFER_ENCODING}; + GROUPS[17] = new String[] {TRANSFER_ENCODING, IF_MODIFIED_SINCE}; GROUPS[18] = new String[] {PROXY_AUTHENTICATE}; - GROUPS[19] = new String[] {CONTENT_DISPOSITION, IF_UNMODIFIED_SINCE, PROXY_AUTHORIZATION}; + GROUPS[19] = new String[] {PROXY_AUTHORIZATION, CONTENT_DISPOSITION, IF_UNMODIFIED_SINCE}; + GROUPS[20] = new String[] {X_AMZ_SECURITY_TOKEN}; GROUPS[25] = new String[] {STRICT_TRANSPORT_SECURITY}; GROUPS[27] = new String[] {ACCESS_CONTROL_ALLOW_ORIGIN}; } @@ -236,6 +243,7 @@ public static String canonicalize(String name) { case 17 -> matchBucket17(name); case 18 -> matchBucket18(name); case 19 -> matchBucket19(name); + case 20 -> matchBucket20(name); case 25 -> matchBucket25(name); case 27 -> matchBucket27(name); default -> HeaderUtils.normalizeName(name); @@ -332,6 +340,11 @@ private static String matchBucket6(String name) { return EXPECT; } break; + case 'o': // [o]rigin + if (name.regionMatches(true, 0, ORIGIN, 0, 6)) { + return ORIGIN; + } + break; case 's': // [s]erver if (name.regionMatches(true, 0, SERVER, 0, 6)) { return SERVER; @@ -439,17 +452,33 @@ private static String matchBucket10(String name) { return USER_AGENT; } break; + case 'x': // [x]-amz-date + if (name.regionMatches(true, 0, X_AMZ_DATE, 0, 10)) { + return X_AMZ_DATE; + } + break; } return HeaderUtils.normalizeName(name); } + // Discriminator: charAt(0) private static String matchBucket11(String name) { - if (name.regionMatches(true, 0, RETRY_AFTER, 0, 11)) { - return RETRY_AFTER; + switch (name.charAt(0) | 0x20) { + case 'c': // [c]ontent-md5 + if (name.regionMatches(true, 0, CONTENT_MD5, 0, 11)) { + return CONTENT_MD5; + } + break; + case 'r': // [r]etry-after + if (name.regionMatches(true, 0, RETRY_AFTER, 0, 11)) { + return RETRY_AFTER; + } + break; } return HeaderUtils.normalizeName(name); } + // Discriminator: charAt(0) private static String matchBucket12(String name) { switch (name.charAt(0) | 0x20) { case 'c': // [c]ontent-type @@ -462,6 +491,11 @@ private static String matchBucket12(String name) { return MAX_FORWARDS; } break; + case 'x': // [x]-amz-target + if (name.regionMatches(true, 0, X_AMZ_TARGET, 0, 12)) { + return X_AMZ_TARGET; + } + break; } return HeaderUtils.normalizeName(name); } @@ -530,6 +564,11 @@ private static String matchBucket15(String name) { return ACCEPT_LANGUAGE; } break; + case 'p': // smithy-[p]rotocol + if (name.regionMatches(true, 0, SMITHY_PROTOCOL, 0, 15)) { + return SMITHY_PROTOCOL; + } + break; case 'r': // amz-sdk-[r]equest if (name.regionMatches(true, 0, AMZ_SDK_REQUEST, 0, 15)) { return AMZ_SDK_REQUEST; @@ -629,6 +668,13 @@ private static String matchBucket19(String name) { return HeaderUtils.normalizeName(name); } + private static String matchBucket20(String name) { + if (name.regionMatches(true, 0, X_AMZ_SECURITY_TOKEN, 0, 20)) { + return X_AMZ_SECURITY_TOKEN; + } + return HeaderUtils.normalizeName(name); + } + private static String matchBucket25(String name) { if (name.regionMatches(true, 0, STRICT_TRANSPORT_SECURITY, 0, 25)) { return STRICT_TRANSPORT_SECURITY; diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java index 6fdbeca04..8b3e8c150 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/ModifiableHttpResponseImpl.java @@ -77,10 +77,10 @@ public ModifiableHttpResponse setBody(DataStream body) { static void addBodyHeaders(DataStream body, ModifiableHttpHeaders headers) { var ct = body.contentType(); if (ct != null) { - headers.setHeaderIfAbsent("content-type", ct); + headers.setHeaderIfAbsent(HeaderNames.CONTENT_TYPE, ct); } if (body.hasKnownLength()) { - headers.setHeaderIfAbsent("content-length", String.valueOf(body.contentLength())); + headers.setHeaderIfAbsent(HeaderNames.CONTENT_LENGTH, String.valueOf(body.contentLength())); } } diff --git a/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java b/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java index 89425cf79..cebe1ac56 100644 --- a/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java +++ b/mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java @@ -15,6 +15,7 @@ import software.amazon.smithy.java.client.http.JavaHttpClientTransport; import software.amazon.smithy.java.context.Context; import software.amazon.smithy.java.core.serde.document.Document; +import software.amazon.smithy.java.http.api.HeaderNames; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.http.api.HttpResponse; import software.amazon.smithy.java.io.ByteBufferUtils; @@ -111,14 +112,14 @@ public CompletableFuture rpc(JsonRpcRequest request) { var requestBuilder = HttpRequest.create() .setUri(endpoint) .setMethod("POST") - .addHeader("Content-Type", "application/json") - .addHeader("Accept", "application/json, text/event-stream") - .addHeader("MCP-Protocol-Version", protocolVersionHeader); + .addHeader(HeaderNames.CONTENT_TYPE, "application/json") + .addHeader(HeaderNames.ACCEPT, "application/json, text/event-stream") + .addHeader("mcp-protocol-version", protocolVersionHeader); // Include session ID if we have one String currentSessionId = sessionId; if (currentSessionId != null) { - requestBuilder.addHeader("Mcp-Session-Id", currentSessionId); + requestBuilder.addHeader("mcp-session-id", currentSessionId); LOG.debug("Including session ID in request: method={}, sessionId={}", request.getMethod(), currentSessionId); diff --git a/server/server-core/src/main/java/software/amazon/smithy/java/server/core/CorsHeaders.java b/server/server-core/src/main/java/software/amazon/smithy/java/server/core/CorsHeaders.java index 2c856bda7..568904a2b 100644 --- a/server/server-core/src/main/java/software/amazon/smithy/java/server/core/CorsHeaders.java +++ b/server/server-core/src/main/java/software/amazon/smithy/java/server/core/CorsHeaders.java @@ -9,34 +9,30 @@ import java.util.Arrays; import java.util.List; -import java.util.Map; +import software.amazon.smithy.java.http.api.HeaderNames; public final class CorsHeaders { private CorsHeaders() {} - private static final Map> BASE_CORS_HEADERS = Map.of( - "Access-Control-Allow-Methods", - List.of("GET, POST, PUT, DELETE, OPTIONS"), - "Access-Control-Allow-Headers", - List.of("*,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Content-Length,Content-Type,X-Amz-User-Agent,X-Amzn-Trace-Id"), - "Access-Control-Max-Age", - List.of("600")); - public static void addCorsHeaders(HttpJob job) { if (!shouldAddCorsHeaders(job)) { return; } - String requestOrigin = job.request().headers().firstValue("origin"); + String requestOrigin = job.request().headers().firstValue(HeaderNames.ORIGIN); String configuredOrigin = getConfiguredOrigin(job); if (!isOriginAllowed(configuredOrigin, requestOrigin)) { return; } - job.response().headers().addHeaders(BASE_CORS_HEADERS); - job.response().headers().addHeader("Access-Control-Allow-Origin", List.of(requestOrigin)); + var headers = job.response().headers(); + headers.addHeader("access-control-allow-methods", "GET, POST, PUT, DELETE, OPTIONS"); + headers.addHeader("access-control-allow-headers", + "*,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Content-Length,Content-Type,X-Amz-User-Agent,X-Amzn-Trace-Id"); + headers.addHeader("access-control-max-age", "600"); + headers.addHeader(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, List.of(requestOrigin)); } private static boolean shouldAddCorsHeaders(HttpJob job) { @@ -46,7 +42,7 @@ private static boolean shouldAddCorsHeaders(HttpJob job) { !job.operation().getApiOperation().service().schema().hasTrait(CORS_TRAIT)) { return false; } - return job.request().headers().hasHeader("origin"); + return job.request().headers().hasHeader(HeaderNames.ORIGIN); } private static String getConfiguredOrigin(HttpJob job) { diff --git a/server/server-core/src/test/java/software/amazon/smithy/java/server/core/CorsHeadersTest.java b/server/server-core/src/test/java/software/amazon/smithy/java/server/core/CorsHeadersTest.java index 05eeded37..647fd82c5 100644 --- a/server/server-core/src/test/java/software/amazon/smithy/java/server/core/CorsHeadersTest.java +++ b/server/server-core/src/test/java/software/amazon/smithy/java/server/core/CorsHeadersTest.java @@ -110,17 +110,17 @@ public Schema schema() { } private void assertContainsHeader(ModifiableHttpHeaders responseHeaders, String allowedOrigin) { - assertTrue(responseHeaders.hasHeader("Access-Control-Allow-Methods")); - assertTrue(responseHeaders.hasHeader("Access-Control-Allow-Headers")); - assertTrue(responseHeaders.hasHeader("Access-Control-Max-Age")); - assertTrue(responseHeaders.hasHeader("Access-Control-Allow-Origin")); - assertTrue(responseHeaders.allValues("Access-Control-Allow-Origin").contains(allowedOrigin)); + assertTrue(responseHeaders.hasHeader("access-control-allow-methods")); + assertTrue(responseHeaders.hasHeader("access-control-allow-headers")); + assertTrue(responseHeaders.hasHeader("access-control-max-age")); + assertTrue(responseHeaders.hasHeader("access-control-allow-origin")); + assertTrue(responseHeaders.allValues("access-control-allow-origin").contains(allowedOrigin)); } private void assertDoNotContainsHeader(ModifiableHttpHeaders responseHeaders) { - assertFalse(responseHeaders.hasHeader("Access-Control-Allow-Methods")); - assertFalse(responseHeaders.hasHeader("Access-Control-Allow-Headers")); - assertFalse(responseHeaders.hasHeader("Access-Control-Max-Age")); - assertFalse(responseHeaders.hasHeader("Access-Control-Allow-Origin")); + assertFalse(responseHeaders.hasHeader("access-control-allow-methods")); + assertFalse(responseHeaders.hasHeader("access-control-allow-headers")); + assertFalse(responseHeaders.hasHeader("access-control-max-age")); + assertFalse(responseHeaders.hasHeader("access-control-allow-origin")); } } From e8cce00e37c0027b584afba8323ab0158fb1f1f9 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Wed, 1 Apr 2026 10:56:26 -0500 Subject: [PATCH 4/4] Add identity check before regionMatches --- config/spotbugs/filter.xml | 7 +- .../smithy/java/http/api/HeaderNames.java | 136 +++++++++--------- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/config/spotbugs/filter.xml b/config/spotbugs/filter.xml index 366cd8b13..726a3067a 100644 --- a/config/spotbugs/filter.xml +++ b/config/spotbugs/filter.xml @@ -102,10 +102,15 @@ - + + + + + + diff --git a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java index 0c5f7b33c..e13ac70f3 100644 --- a/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java +++ b/http/http-api/src/main/java/software/amazon/smithy/java/http/api/HeaderNames.java @@ -250,15 +250,19 @@ public static String canonicalize(String name) { }; } + private static boolean isMatch(String name, String candidate, int length) { + return name == candidate || name.regionMatches(true, 0, candidate, 0, length); + } + private static String matchBucket3(String name) { switch (name.charAt(0) | 0x20) { case 'a': // [a]ge - if (name.regionMatches(true, 0, AGE, 0, 3)) { + if (isMatch(name, AGE, 3)) { return AGE; } break; case 'v': // [v]ia - if (name.regionMatches(true, 0, VIA, 0, 3)) { + if (isMatch(name, VIA, 3)) { return VIA; } break; @@ -269,32 +273,32 @@ private static String matchBucket3(String name) { private static String matchBucket4(String name) { switch (name.charAt(0) | 0x20) { case 'h': // [h]ost - if (name.regionMatches(true, 0, HOST, 0, 4)) { + if (isMatch(name, HOST, 4)) { return HOST; } break; case 'd': // [d]ate - if (name.regionMatches(true, 0, DATE, 0, 4)) { + if (isMatch(name, DATE, 4)) { return DATE; } break; case 'e': // [e]tag - if (name.regionMatches(true, 0, ETAG, 0, 4)) { + if (isMatch(name, ETAG, 4)) { return ETAG; } break; case 'f': // [f]rom - if (name.regionMatches(true, 0, FROM, 0, 4)) { + if (isMatch(name, FROM, 4)) { return FROM; } break; case 'l': // [l]ink - if (name.regionMatches(true, 0, LINK, 0, 4)) { + if (isMatch(name, LINK, 4)) { return LINK; } break; case 'v': // [v]ary - if (name.regionMatches(true, 0, VARY, 0, 4)) { + if (isMatch(name, VARY, 4)) { return VARY; } break; @@ -305,17 +309,17 @@ private static String matchBucket4(String name) { private static String matchBucket5(String name) { switch (name.charAt(0) | 0x20) { case ':': // [:path] - if (name.regionMatches(true, 0, PSEUDO_PATH, 0, 5)) { + if (isMatch(name, PSEUDO_PATH, 5)) { return PSEUDO_PATH; } break; case 'a': // [a]llow - if (name.regionMatches(true, 0, ALLOW, 0, 5)) { + if (isMatch(name, ALLOW, 5)) { return ALLOW; } break; case 'r': // [r]ange - if (name.regionMatches(true, 0, RANGE, 0, 5)) { + if (isMatch(name, RANGE, 5)) { return RANGE; } break; @@ -326,27 +330,27 @@ private static String matchBucket5(String name) { private static String matchBucket6(String name) { switch (name.charAt(0) | 0x20) { case 'a': // [a]ccept - if (name.regionMatches(true, 0, ACCEPT, 0, 6)) { + if (isMatch(name, ACCEPT, 6)) { return ACCEPT; } break; case 'c': // [c]ookie - if (name.regionMatches(true, 0, COOKIE, 0, 6)) { + if (isMatch(name, COOKIE, 6)) { return COOKIE; } break; case 'e': // [e]xpect - if (name.regionMatches(true, 0, EXPECT, 0, 6)) { + if (isMatch(name, EXPECT, 6)) { return EXPECT; } break; case 'o': // [o]rigin - if (name.regionMatches(true, 0, ORIGIN, 0, 6)) { + if (isMatch(name, ORIGIN, 6)) { return ORIGIN; } break; case 's': // [s]erver - if (name.regionMatches(true, 0, SERVER, 0, 6)) { + if (isMatch(name, SERVER, 6)) { return SERVER; } break; @@ -357,46 +361,46 @@ private static String matchBucket6(String name) { private static String matchBucket7(String name) { switch (name.charAt(2) | 0x20) { case 'a': // tr[a]iler - if (name.regionMatches(true, 0, TRAILER, 0, 7)) { + if (isMatch(name, TRAILER, 7)) { return TRAILER; } break; case 'c': // ":s[c]heme" - if (name.regionMatches(true, 0, PSEUDO_SCHEME, 0, 7)) { + if (isMatch(name, PSEUDO_SCHEME, 7)) { return PSEUDO_SCHEME; } break; case 'e': // ":m[e]thod" - if (name.regionMatches(true, 0, PSEUDO_METHOD, 0, 7)) { + if (isMatch(name, PSEUDO_METHOD, 7)) { return PSEUDO_METHOD; } break; case 'f': // "re[f]erer" / "re[f]resh" switch (name.charAt(3) | 0x20) { case 'e': // "ref(e)rer" - if (name.regionMatches(true, 0, REFERER, 0, 7)) { + if (isMatch(name, REFERER, 7)) { return REFERER; } break; case 'r': // "ref[r]esh" - if (name.regionMatches(true, 0, REFRESH, 0, 7)) { + if (isMatch(name, REFRESH, 7)) { return REFRESH; } break; } break; case 'g': // "up[g]rade" - if (name.regionMatches(true, 0, UPGRADE, 0, 7)) { + if (isMatch(name, UPGRADE, 7)) { return UPGRADE; } break; case 'p': // "ex[p]ires" - if (name.regionMatches(true, 0, EXPIRES, 0, 7)) { + if (isMatch(name, EXPIRES, 7)) { return EXPIRES; } break; case 't': // ":s[t]atus" - if (name.regionMatches(true, 0, PSEUDO_STATUS, 0, 7)) { + if (isMatch(name, PSEUDO_STATUS, 7)) { return PSEUDO_STATUS; } break; @@ -407,17 +411,17 @@ private static String matchBucket7(String name) { private static String matchBucket8(String name) { switch (name.charAt(3) | 0x20) { case 'a': // loc[a]tion - if (name.regionMatches(true, 0, LOCATION, 0, 8)) { + if (isMatch(name, LOCATION, 8)) { return LOCATION; } break; case 'm': // if-[m]atch - if (name.regionMatches(true, 0, IF_MATCH, 0, 8)) { + if (isMatch(name, IF_MATCH, 8)) { return IF_MATCH; } break; case 'r': // if-[r]ange - if (name.regionMatches(true, 0, IF_RANGE, 0, 8)) { + if (isMatch(name, IF_RANGE, 8)) { return IF_RANGE; } break; @@ -428,32 +432,32 @@ private static String matchBucket8(String name) { private static String matchBucket10(String name) { switch (name.charAt(0) | 0x20) { case ':': // [:]authority - if (name.regionMatches(true, 0, PSEUDO_AUTHORITY, 0, 10)) { + if (isMatch(name, PSEUDO_AUTHORITY, 10)) { return PSEUDO_AUTHORITY; } break; case 'c': // [c]onnection - if (name.regionMatches(true, 0, CONNECTION, 0, 10)) { + if (isMatch(name, CONNECTION, 10)) { return CONNECTION; } break; case 'k': // [k]eep-alive - if (name.regionMatches(true, 0, KEEP_ALIVE, 0, 10)) { + if (isMatch(name, KEEP_ALIVE, 10)) { return KEEP_ALIVE; } break; case 's': // [s]et-cookie - if (name.regionMatches(true, 0, SET_COOKIE, 0, 10)) { + if (isMatch(name, SET_COOKIE, 10)) { return SET_COOKIE; } break; case 'u': // [u]ser-agent - if (name.regionMatches(true, 0, USER_AGENT, 0, 10)) { + if (isMatch(name, USER_AGENT, 10)) { return USER_AGENT; } break; case 'x': // [x]-amz-date - if (name.regionMatches(true, 0, X_AMZ_DATE, 0, 10)) { + if (isMatch(name, X_AMZ_DATE, 10)) { return X_AMZ_DATE; } break; @@ -470,7 +474,7 @@ private static String matchBucket11(String name) { } break; case 'r': // [r]etry-after - if (name.regionMatches(true, 0, RETRY_AFTER, 0, 11)) { + if (isMatch(name, RETRY_AFTER, 11)) { return RETRY_AFTER; } break; @@ -482,17 +486,17 @@ private static String matchBucket11(String name) { private static String matchBucket12(String name) { switch (name.charAt(0) | 0x20) { case 'c': // [c]ontent-type - if (name.regionMatches(true, 0, CONTENT_TYPE, 0, 12)) { + if (isMatch(name, CONTENT_TYPE, 12)) { return CONTENT_TYPE; } break; case 'm': // [m]ax-forwards - if (name.regionMatches(true, 0, MAX_FORWARDS, 0, 12)) { + if (isMatch(name, MAX_FORWARDS, 12)) { return MAX_FORWARDS; } break; case 'x': // [x]-amz-target - if (name.regionMatches(true, 0, X_AMZ_TARGET, 0, 12)) { + if (isMatch(name, X_AMZ_TARGET, 12)) { return X_AMZ_TARGET; } break; @@ -503,32 +507,32 @@ private static String matchBucket12(String name) { private static String matchBucket13(String name) { switch (name.charAt(6) | 0x20) { case '-': // accept[-]ranges - if (name.regionMatches(true, 0, ACCEPT_RANGES, 0, 13)) { + if (isMatch(name, ACCEPT_RANGES, 13)) { return ACCEPT_RANGES; } break; case 'c': // cache-[c]ontrol - if (name.regionMatches(true, 0, CACHE_CONTROL, 0, 13)) { + if (isMatch(name, CACHE_CONTROL, 13)) { return CACHE_CONTROL; } break; case 'e': // if-non[e]-match - if (name.regionMatches(true, 0, IF_NONE_MATCH, 0, 13)) { + if (isMatch(name, IF_NONE_MATCH, 13)) { return IF_NONE_MATCH; } break; case 'i': // author[i]zation - if (name.regionMatches(true, 0, AUTHORIZATION, 0, 13)) { + if (isMatch(name, AUTHORIZATION, 13)) { return AUTHORIZATION; } break; case 'o': // last-m[o]dified - if (name.regionMatches(true, 0, LAST_MODIFIED, 0, 13)) { + if (isMatch(name, LAST_MODIFIED, 13)) { return LAST_MODIFIED; } break; case 't': // conten[t]-range - if (name.regionMatches(true, 0, CONTENT_RANGE, 0, 13)) { + if (isMatch(name, CONTENT_RANGE, 13)) { return CONTENT_RANGE; } break; @@ -539,12 +543,12 @@ private static String matchBucket13(String name) { private static String matchBucket14(String name) { switch (name.charAt(0) | 0x20) { case 'c': // [c]ontent-length - if (name.regionMatches(true, 0, CONTENT_LENGTH, 0, 14)) { + if (isMatch(name, CONTENT_LENGTH, 14)) { return CONTENT_LENGTH; } break; case 'a': // [a]ccept-charset - if (name.regionMatches(true, 0, ACCEPT_CHARSET, 0, 14)) { + if (isMatch(name, ACCEPT_CHARSET, 14)) { return ACCEPT_CHARSET; } break; @@ -555,27 +559,27 @@ private static String matchBucket14(String name) { private static String matchBucket15(String name) { switch (name.charAt(7) | 0x20) { case 'e': // accept-[e]ncoding - if (name.regionMatches(true, 0, ACCEPT_ENCODING, 0, 15)) { + if (isMatch(name, ACCEPT_ENCODING, 15)) { return ACCEPT_ENCODING; } break; case 'l': // accept-[l]anguage - if (name.regionMatches(true, 0, ACCEPT_LANGUAGE, 0, 15)) { + if (isMatch(name, ACCEPT_LANGUAGE, 15)) { return ACCEPT_LANGUAGE; } break; case 'p': // smithy-[p]rotocol - if (name.regionMatches(true, 0, SMITHY_PROTOCOL, 0, 15)) { + if (isMatch(name, SMITHY_PROTOCOL, 15)) { return SMITHY_PROTOCOL; } break; case 'r': // amz-sdk-[r]equest - if (name.regionMatches(true, 0, AMZ_SDK_REQUEST, 0, 15)) { + if (isMatch(name, AMZ_SDK_REQUEST, 15)) { return AMZ_SDK_REQUEST; } break; case 't': // x-amzn-[t]race-id - if (name.regionMatches(true, 0, X_AMZN_TRACE_ID, 0, 15)) { + if (isMatch(name, X_AMZN_TRACE_ID, 15)) { return X_AMZN_TRACE_ID; } break; @@ -586,37 +590,37 @@ private static String matchBucket15(String name) { private static String matchBucket16(String name) { switch (name.charAt(11) | 0x20) { case 'a': // content-loc[a]tion - if (name.regionMatches(true, 0, CONTENT_LOCATION, 0, 16)) { + if (isMatch(name, CONTENT_LOCATION, 16)) { return CONTENT_LOCATION; } break; case 'c': // proxy-conne[c]tion - if (name.regionMatches(true, 0, PROXY_CONNECTION, 0, 16)) { + if (isMatch(name, PROXY_CONNECTION, 16)) { return PROXY_CONNECTION; } break; case 'e': // x-amzn-requ[e]stid - if (name.regionMatches(true, 0, X_AMZN_REQUESTID, 0, 16)) { + if (isMatch(name, X_AMZN_REQUESTID, 16)) { return X_AMZN_REQUESTID; } break; case 'g': // content-lan[g]uage - if (name.regionMatches(true, 0, CONTENT_LANGUAGE, 0, 16)) { + if (isMatch(name, CONTENT_LANGUAGE, 16)) { return CONTENT_LANGUAGE; } break; case 'i': // www-authent[i]cate - if (name.regionMatches(true, 0, WWW_AUTHENTICATE, 0, 16)) { + if (isMatch(name, WWW_AUTHENTICATE, 16)) { return WWW_AUTHENTICATE; } break; case 'o': // content-enc[o]ding - if (name.regionMatches(true, 0, CONTENT_ENCODING, 0, 16)) { + if (isMatch(name, CONTENT_ENCODING, 16)) { return CONTENT_ENCODING; } break; case 's': // x-amz-reque[s]t-id - if (name.regionMatches(true, 0, X_AMZ_REQUEST_ID, 0, 16)) { + if (isMatch(name, X_AMZ_REQUEST_ID, 16)) { return X_AMZ_REQUEST_ID; } break; @@ -627,12 +631,12 @@ private static String matchBucket16(String name) { private static String matchBucket17(String name) { switch (name.charAt(0) | 0x20) { case 'i': // [i]f-modified-since - if (name.regionMatches(true, 0, IF_MODIFIED_SINCE, 0, 17)) { + if (isMatch(name, IF_MODIFIED_SINCE, 17)) { return IF_MODIFIED_SINCE; } break; case 't': // [t]ransfer-encoding - if (name.regionMatches(true, 0, TRANSFER_ENCODING, 0, 17)) { + if (isMatch(name, TRANSFER_ENCODING, 17)) { return TRANSFER_ENCODING; } break; @@ -641,7 +645,7 @@ private static String matchBucket17(String name) { } private static String matchBucket18(String name) { - if (name.regionMatches(true, 0, PROXY_AUTHENTICATE, 0, 18)) { + if (isMatch(name, PROXY_AUTHENTICATE, 18)) { return PROXY_AUTHENTICATE; } return HeaderUtils.normalizeName(name); @@ -650,17 +654,17 @@ private static String matchBucket18(String name) { private static String matchBucket19(String name) { switch (name.charAt(0) | 0x20) { case 'c': // [c]ontent-disposition - if (name.regionMatches(true, 0, CONTENT_DISPOSITION, 0, 19)) { + if (isMatch(name, CONTENT_DISPOSITION, 19)) { return CONTENT_DISPOSITION; } break; case 'i': // [i]f-unmodified-since - if (name.regionMatches(true, 0, IF_UNMODIFIED_SINCE, 0, 19)) { + if (isMatch(name, IF_UNMODIFIED_SINCE, 19)) { return IF_UNMODIFIED_SINCE; } break; case 'p': // [p]roxy-authorization - if (name.regionMatches(true, 0, PROXY_AUTHORIZATION, 0, 19)) { + if (isMatch(name, PROXY_AUTHORIZATION, 19)) { return PROXY_AUTHORIZATION; } break; @@ -669,21 +673,21 @@ private static String matchBucket19(String name) { } private static String matchBucket20(String name) { - if (name.regionMatches(true, 0, X_AMZ_SECURITY_TOKEN, 0, 20)) { + if (isMatch(name, X_AMZ_SECURITY_TOKEN, 20)) { return X_AMZ_SECURITY_TOKEN; } return HeaderUtils.normalizeName(name); } private static String matchBucket25(String name) { - if (name.regionMatches(true, 0, STRICT_TRANSPORT_SECURITY, 0, 25)) { + if (isMatch(name, STRICT_TRANSPORT_SECURITY, 25)) { return STRICT_TRANSPORT_SECURITY; } return HeaderUtils.normalizeName(name); } private static String matchBucket27(String name) { - if (name.regionMatches(true, 0, ACCESS_CONTROL_ALLOW_ORIGIN, 0, 27)) { + if (isMatch(name, ACCESS_CONTROL_ALLOW_ORIGIN, 27)) { return ACCESS_CONTROL_ALLOW_ORIGIN; } return HeaderUtils.normalizeName(name);