From 84775786c3c893438b0327a1b7dd41906395fb41 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Wed, 7 Feb 2024 13:26:59 -0800 Subject: [PATCH] Handle header lists in URLConnection client --- .../bugfix-URLConnectionClient-71a8135.json | 6 ++ .../UrlConnectionHttpClient.java | 9 ++- .../http/urlconnection/HeadersListTest.java | 73 +++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .changes/next-release/bugfix-URLConnectionClient-71a8135.json create mode 100644 http-clients/url-connection-client/src/test/java/software/amazon/awssdk/http/urlconnection/HeadersListTest.java diff --git a/.changes/next-release/bugfix-URLConnectionClient-71a8135.json b/.changes/next-release/bugfix-URLConnectionClient-71a8135.json new file mode 100644 index 000000000000..18fea0242497 --- /dev/null +++ b/.changes/next-release/bugfix-URLConnectionClient-71a8135.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "URL Connection Client", + "contributor": "", + "description": "Fix a bug where headers with multiple values don't have all values for that header sent on the wire. This leads to signature mismatch exceptions.\n\nFixes [#4746](https://github.com/aws/aws-sdk-java-v2/issues/4746)." +} diff --git a/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.java b/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.java index e4bde8ffd958..8268cd3bfb60 100644 --- a/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.java +++ b/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.java @@ -152,7 +152,7 @@ public String clientName() { private HttpURLConnection createAndConfigureConnection(HttpExecuteRequest request) { SdkHttpRequest sdkHttpRequest = request.httpRequest(); HttpURLConnection connection = connectionFactory.createConnection(sdkHttpRequest.getUri()); - sdkHttpRequest.forEachHeader((key, values) -> values.forEach(value -> connection.setRequestProperty(key, value))); + setHeaders(connection, sdkHttpRequest); // connection.setRequestProperty("Transfer-Encoding", "chunked") does not work, i.e., property does not get set if (sdkHttpRequest.matchingHeaders("Transfer-Encoding").contains("chunked")) { @@ -180,6 +180,13 @@ private HttpURLConnection createAndConfigureConnection(HttpExecuteRequest reques return connection; } + private void setHeaders(HttpURLConnection connection, SdkHttpRequest request) { + request.forEachHeader((name, values) -> { + String commaSeparated = String.join(",", values); + connection.addRequestProperty(name, commaSeparated); + }); + } + private HttpURLConnection createDefaultConnection(URI uri, SSLSocketFactory socketFactory) { Optional proxy = determineProxy(uri); diff --git a/http-clients/url-connection-client/src/test/java/software/amazon/awssdk/http/urlconnection/HeadersListTest.java b/http-clients/url-connection-client/src/test/java/software/amazon/awssdk/http/urlconnection/HeadersListTest.java new file mode 100644 index 000000000000..2361fa735fe6 --- /dev/null +++ b/http-clients/url-connection-client/src/test/java/software/amazon/awssdk/http/urlconnection/HeadersListTest.java @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.http.urlconnection; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import com.github.tomakehurst.wiremock.WireMockServer; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.http.ContentStreamProvider; +import software.amazon.awssdk.http.HttpExecuteRequest; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; + +public class HeadersListTest { + private static final WireMockServer WIRE_MOCK = new WireMockServer(0); + private static SdkHttpClient client; + + @BeforeAll + public static void setup() { + WIRE_MOCK.start(); + client = UrlConnectionHttpClient.create(); + } + + @AfterAll + public static void teardown() { + client.close(); + WIRE_MOCK.stop(); + } + + @Test + public void execute_requestHeaderHasMultipleValues_allValuesSent() throws IOException { + ContentStreamProvider provider = () -> new ByteArrayInputStream(new byte[0]); + SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder() + .method(SdkHttpMethod.PUT) + .host("localhost") + .port(WIRE_MOCK.port()) + .protocol("http") + .putHeader("my-header", Arrays.asList("value1", "value2")) + .encodedPath("/test") + .build(); + + HttpExecuteRequest request = HttpExecuteRequest.builder() + .request(httpRequest) + .contentStreamProvider(provider) + .build(); + + client.prepareRequest(request).call(); + + WIRE_MOCK.verify(putRequestedFor(urlEqualTo("/test")) + .withHeader("my-header", equalTo("value1,value2"))); + } +}