From 554662ebab87af97ba25d0c9f5449c7acda8df9c Mon Sep 17 00:00:00 2001 From: sdeleuze Date: Fri, 8 Dec 2017 12:42:16 -0800 Subject: [PATCH] Allow interceptors to add existing header values Provide a fully mutable HttpHeaders to ClientHttpRequestInterceptors of a RestTemplate when headers are set using HttpEntity. This avoids UnsupportedOperationException if both HttpEntity and ClientHttpRequestInterceptor add values for the same HTTP header. Issue: SPR-15066 --- .../web/client/RestTemplate.java | 5 +++- .../web/client/RestTemplateTests.java | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 35b7372e9254..a287c4b5031c 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -21,6 +21,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -909,7 +910,9 @@ public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); if (!requestHeaders.isEmpty()) { - httpHeaders.putAll(requestHeaders); + for (Map.Entry> entry : requestHeaders.entrySet()) { + httpHeaders.put(entry.getKey(), new LinkedList<>(entry.getValue())); + } } if (httpHeaders.getContentLength() < 0) { httpHeaders.setContentLength(0L); diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java index e07fa329fc53..8bd8289c9ae7 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java @@ -39,12 +39,15 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.StreamUtils; import org.springframework.web.util.DefaultUriBuilderFactory; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; import static org.springframework.http.HttpMethod.POST; @@ -822,4 +825,30 @@ public void exchangeParameterizedType() throws Exception { verify(response).close(); } + @Test // SPR-15066 + public void requestInterceptorCanAddExistingHeaderValue() throws Exception { + ClientHttpRequestInterceptor interceptor = (request, body, execution) -> { + request.getHeaders().add("MyHeader", "MyInterceptorValue"); + return execution.execute(request, body); + }; + template.setInterceptors(Collections.singletonList(interceptor)); + + given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).willReturn(request); + HttpHeaders requestHeaders = new HttpHeaders(); + given(request.getHeaders()).willReturn(requestHeaders); + given(request.execute()).willReturn(response); + given(errorHandler.hasError(response)).willReturn(false); + HttpStatus status = HttpStatus.OK; + given(response.getStatusCode()).willReturn(status); + given(response.getStatusText()).willReturn(status.getReasonPhrase()); + + HttpHeaders entityHeaders = new HttpHeaders(); + entityHeaders.add("MyHeader", "MyEntityValue"); + HttpEntity entity = new HttpEntity<>(null, entityHeaders); + template.exchange("http://example.com", HttpMethod.POST, entity, Void.class); + assertThat(requestHeaders.get("MyHeader"), contains("MyEntityValue", "MyInterceptorValue")); + + verify(response).close(); + } + }