diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpClientProtocol.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpClientProtocol.java index dca3e784e..a717de466 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpClientProtocol.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpClientProtocol.java @@ -36,13 +36,14 @@ public MessageExchange messageExchange() { @Override public HttpRequest setServiceEndpoint(HttpRequest request, Endpoint endpoint) { var merged = request.uri().withEndpoint(endpoint.uri()); - var requestBuilder = request.toBuilder(); + var modifiableRequest = request.toModifiable(); + modifiableRequest.setUri(merged); // Merge in any HTTP headers found on the endpoint. if (endpoint.property(HttpContext.ENDPOINT_RESOLVER_HTTP_HEADERS) != null) { - requestBuilder.withAddedHeaders(endpoint.property(HttpContext.ENDPOINT_RESOLVER_HTTP_HEADERS)); + modifiableRequest.headers().addHeaders(endpoint.property(HttpContext.ENDPOINT_RESOLVER_HTTP_HEADERS)); } - return requestBuilder.uri(merged).build(); + return modifiableRequest; } } diff --git a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/UserAgentPlugin.java b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/UserAgentPlugin.java index 7ff047f80..7b584b9d4 100644 --- a/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/UserAgentPlugin.java +++ b/client/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/UserAgentPlugin.java @@ -5,7 +5,6 @@ package software.amazon.smithy.java.client.http.plugins; -import java.util.List; import java.util.Locale; import software.amazon.smithy.java.client.core.AutoClientPlugin; import software.amazon.smithy.java.client.core.CallContext; @@ -78,10 +77,9 @@ static final class UserAgentInterceptor implements ClientInterceptor { @Override public RequestT modifyBeforeSigning(RequestHook hook) { if (hook.request() instanceof HttpRequest req && !req.headers().hasHeader("user-agent")) { - return hook.asRequestType( - req.toBuilder() - .withReplacedHeader("user-agent", List.of(createUa(hook.context()))) - .build()); + var updated = req.toModifiable(); + updated.headers().setHeader("user-agent", createUa(hook.context())); + return hook.asRequestType(updated); } return hook.request(); } 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 ea1d1ea87..3895f334b 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 @@ -242,7 +242,7 @@ public HttpResponse send(Context context, HttpRequest request) { // Track the request here rather than when the request is created because this method is called // more often than the request creation method when retries happen. if (trackRequests) { - requests.add(currentRequest.request()); + requests.add(currentRequest.request().withRequest(request.toUnmodifiable())); } MockedResult result = null; 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 2497bba6c..0463cad5f 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 @@ -150,8 +150,10 @@ default B body(Flow.Publisher publisher) { * @param headers Headers to add and merge in. * @return the builder. */ + @SuppressWarnings("unchecked") default B withAddedHeaders(HttpHeaders headers) { - return withAddedHeaders(headers.map()); + headers.forEachEntry(this::withAddedHeader); + return (B) this; } /** @@ -168,15 +170,26 @@ default B withAddedHeaders(HttpHeaders headers) { * @param headers Headers to put. * @return the builder. */ + @SuppressWarnings("unchecked") default B withReplacedHeaders(HttpHeaders headers) { - return withReplacedHeaders(headers.map()); + headers.forEachEntry(this::withReplacedHeader); + return (B) this; } /** * Replaces a header. * * @param name Header name to replace. - * @param values Header value 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) { 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 15ceea7b3..6dac05912 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 @@ -93,6 +93,12 @@ public Builder withReplacedHeaders(Map> 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"); 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 7067ca86d..3a4042970 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 @@ -81,6 +81,12 @@ public Builder withReplacedHeaders(Map> 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();