getResponseHeaders(String name) throws IOException {
- Header[] headers = httpResponse.getHeaders(name);
- String[] values = new String[headers.length];
- for (int i = 0; i < headers.length; i++) {
- values[i] = headers[i].getValue();
- }
- return Arrays.asList(values).iterator();
+
+ return Arrays.stream(httpResponse.getHeaders(name)) //
+ .map(NameValuePair::getValue) //
+ .iterator();
}
}
diff --git a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpComponents5MessageSender.java b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpComponents5MessageSender.java
index 22caf2f13..27bc7c8e5 100644
--- a/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpComponents5MessageSender.java
+++ b/spring-ws-core/src/main/java/org/springframework/ws/transport/http/HttpComponents5MessageSender.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.net.URI;
+import java.time.Duration;
import java.util.Map;
import org.apache.hc.client5.http.auth.AuthScope;
@@ -33,7 +34,6 @@
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.protocol.HttpContext;
-
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -57,7 +57,9 @@
*/
public class HttpComponents5MessageSender extends AbstractHttpWebServiceMessageSender
implements InitializingBean, DisposableBean {
+
private static final String HTTP_CLIENT_ALREADY_SET = "httpClient already set";
+
private HttpClient httpClient;
private HttpComponents5ClientFactory clientFactory;
@@ -67,22 +69,23 @@ public class HttpComponents5MessageSender extends AbstractHttpWebServiceMessageS
* {@link PoolingHttpClientConnectionManager}.
*/
public HttpComponents5MessageSender() {
+
this.clientFactory = new HttpComponents5ClientFactory();
- this.clientFactory.setClientBuilderCustomizer(httpClientBuilder ->
- httpClientBuilder.addRequestInterceptorFirst(new RemoveSoapHeadersInterceptor()));
+ this.clientFactory.setClientBuilderCustomizer(
+ httpClientBuilder -> httpClientBuilder.addRequestInterceptorFirst(new RemoveSoapHeadersInterceptor()));
}
/**
- * Create a new instance of the {@code HttpClientMessageSender} with the given {@link HttpClient} instance.
+ * Create a new instance of the {@link HttpComponents5MessageSender} with the given {@link HttpClient} instance.
*
* This constructor does not change the given {@code HttpClient} in any way. As such, it does not set timeouts, nor
- * does it
- * {@linkplain HttpClientBuilder#addRequestInterceptorFirst(HttpRequestInterceptor)
- * add} the {@link RemoveSoapHeadersInterceptor}.
+ * does it {@linkplain HttpClientBuilder#addRequestInterceptorFirst(HttpRequestInterceptor) add} the
+ * {@link RemoveSoapHeadersInterceptor}.
*
* @param httpClient the HttpClient instance to use for this sender
*/
public HttpComponents5MessageSender(HttpClient httpClient) {
+
Assert.notNull(httpClient, "httpClient must not be null");
this.httpClient = httpClient;
}
@@ -91,9 +94,11 @@ public HttpComponents5MessageSender(HttpClient httpClient) {
* @see HttpComponents5ClientFactory#setAuthScope(AuthScope)
*/
public void setAuthScope(AuthScope authScope) {
- if (null != getHttpClient()) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setAuthScope(authScope);
}
@@ -101,9 +106,11 @@ public void setAuthScope(AuthScope authScope) {
* @see HttpComponents5ClientFactory#setCredentials(Credentials)
*/
public void setCredentials(Credentials credentials) {
- if (null != getHttpClient()) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setCredentials(credentials);
}
@@ -122,22 +129,26 @@ public void setHttpClient(HttpClient httpClient) {
}
/**
- * @see HttpComponents5ClientFactory#setConnectionTimeout(int)
+ * @see HttpComponents5ClientFactory#setConnectionTimeout(Duration)
*/
- public void setConnectionTimeout(int timeout) {
- if (null != getHttpClient()) {
+ public void setConnectionTimeout(Duration timeout) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setConnectionTimeout(timeout);
}
/**
- * @see HttpComponents5ClientFactory#setReadTimeout(int)
+ * @see HttpComponents5ClientFactory#setReadTimeout(Duration)
*/
- public void setReadTimeout(int timeout) {
- if (null != getHttpClient()) {
+ public void setReadTimeout(Duration timeout) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setReadTimeout(timeout);
}
@@ -145,9 +156,11 @@ public void setReadTimeout(int timeout) {
* @see HttpComponents5ClientFactory#setMaxTotalConnections(int)
*/
public void setMaxTotalConnections(int maxTotalConnections) {
- if (null != getHttpClient()) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setMaxTotalConnections(maxTotalConnections);
}
@@ -155,9 +168,11 @@ public void setMaxTotalConnections(int maxTotalConnections) {
* @see HttpComponents5ClientFactory#setMaxConnectionsPerHost(Map)
*/
public void setMaxConnectionsPerHost(Map maxConnectionsPerHost) {
- if (null != getHttpClient()) {
+
+ if (getHttpClient() != null) {
throw new IllegalStateException(HTTP_CLIENT_ALREADY_SET);
}
+
this.clientFactory.setMaxConnectionsPerHost(maxConnectionsPerHost);
}
@@ -168,16 +183,20 @@ public void afterPropertiesSet() throws Exception {
@Override
public WebServiceConnection createConnection(URI uri) throws IOException {
+
HttpPost httpPost = new HttpPost(uri);
+
if (isAcceptGzipEncoding()) {
httpPost.addHeader(HttpTransportConstants.HEADER_ACCEPT_ENCODING, HttpTransportConstants.CONTENT_ENCODING_GZIP);
}
+
HttpContext httpContext = createContext(uri);
+
return new HttpComponents5Connection(getHttpClient(), httpPost, httpContext);
}
/**
- * Template method that allows for creation of a {@link HttpContext} for the given uri. Default implementation returns
+ * Template method that allows for creation of an {@link HttpContext} for the given uri. Default implementation returns
* {@code null}.
*
* @param uri the URI to create the context for
@@ -189,7 +208,8 @@ protected HttpContext createContext(URI uri) {
@Override
public void destroy() throws Exception {
- if (getHttpClient() instanceof CloseableHttpClient client) {
+
+ if (getHttpClient()instanceof CloseableHttpClient client) {
client.close();
}
}
@@ -200,11 +220,15 @@ public void destroy() throws Exception {
* these headers themselves, and HttpClient throws an exception if they have been set.
*/
public static class RemoveSoapHeadersInterceptor implements HttpRequestInterceptor {
+
@Override
- public void process(HttpRequest request, EntityDetails entityDetails, HttpContext httpContext) throws HttpException, IOException {
+ public void process(HttpRequest request, EntityDetails entityDetails, HttpContext httpContext)
+ throws HttpException, IOException {
+
if (request.containsHeader(HttpHeaders.TRANSFER_ENCODING)) {
request.removeHeaders(HttpHeaders.TRANSFER_ENCODING);
}
+
if (request.containsHeader(HttpHeaders.CONTENT_LENGTH)) {
request.removeHeaders(HttpHeaders.CONTENT_LENGTH);
}
diff --git a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpComponents5MessageSenderIntegrationTest.java b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpComponents5MessageSenderIntegrationTest.java
index a83203983..e34d3b852 100644
--- a/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpComponents5MessageSenderIntegrationTest.java
+++ b/spring-ws-core/src/test/java/org/springframework/ws/transport/http/HttpComponents5MessageSenderIntegrationTest.java
@@ -16,15 +16,19 @@
package org.springframework.ws.transport.http;
-import java.io.IOException;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
+import static org.assertj.core.api.AssertionsForClassTypes.*;
+import static org.springframework.ws.transport.http.HttpComponents5ClientFactory.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.xml.soap.MessageFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
@@ -34,15 +38,12 @@
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.jupiter.api.Test;
-
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.util.FileCopyUtils;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.support.FreePortScanner;
-import static org.assertj.core.api.AssertionsForClassTypes.*;
-import static org.springframework.ws.transport.http.HttpComponents5ClientFactory.*;
class HttpComponents5MessageSenderIntegrationTest
extends AbstractHttpWebServiceMessageSenderIntegrationTestCase {
@@ -52,7 +53,7 @@ protected HttpComponents5MessageSender createMessageSender() {
return new HttpComponents5MessageSender();
}
- @Test
+ @Test // GH-1164
void testMaxConnections() throws Exception {
final String url1 = "https://www.example.com";
@@ -103,7 +104,7 @@ void testMaxConnections() throws Exception {
assertThat(poolingHttpClientConnectionManager.getMaxPerRoute(route3)).isEqualTo(10);
}
- @Test
+ @Test // GH-1164
void testContextClose() throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
@@ -138,6 +139,7 @@ void testContextClose() throws Exception {
appContext.close();
} finally {
+
if (connection != null) {
try {
connection.close();