Skip to content

Commit 554662e

Browse files
committed
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
1 parent ac00d8a commit 554662e

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

spring-web/src/main/java/org/springframework/web/client/RestTemplate.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.net.URI;
2222
import java.util.ArrayList;
2323
import java.util.Collections;
24+
import java.util.LinkedList;
2425
import java.util.List;
2526
import java.util.Map;
2627
import java.util.Set;
@@ -909,7 +910,9 @@ public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
909910
HttpHeaders httpHeaders = httpRequest.getHeaders();
910911
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
911912
if (!requestHeaders.isEmpty()) {
912-
httpHeaders.putAll(requestHeaders);
913+
for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
914+
httpHeaders.put(entry.getKey(), new LinkedList<>(entry.getValue()));
915+
}
913916
}
914917
if (httpHeaders.getContentLength() < 0) {
915918
httpHeaders.setContentLength(0L);

spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java

+29
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@
3939
import org.springframework.http.ResponseEntity;
4040
import org.springframework.http.client.ClientHttpRequest;
4141
import org.springframework.http.client.ClientHttpRequestFactory;
42+
import org.springframework.http.client.ClientHttpRequestInterceptor;
4243
import org.springframework.http.client.ClientHttpResponse;
4344
import org.springframework.http.converter.GenericHttpMessageConverter;
4445
import org.springframework.http.converter.HttpMessageConverter;
4546
import org.springframework.util.StreamUtils;
4647
import org.springframework.web.util.DefaultUriBuilderFactory;
4748

49+
import static org.hamcrest.MatcherAssert.assertThat;
50+
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
4851
import static org.junit.Assert.*;
4952
import static org.mockito.BDDMockito.*;
5053
import static org.springframework.http.HttpMethod.POST;
@@ -822,4 +825,30 @@ public void exchangeParameterizedType() throws Exception {
822825
verify(response).close();
823826
}
824827

828+
@Test // SPR-15066
829+
public void requestInterceptorCanAddExistingHeaderValue() throws Exception {
830+
ClientHttpRequestInterceptor interceptor = (request, body, execution) -> {
831+
request.getHeaders().add("MyHeader", "MyInterceptorValue");
832+
return execution.execute(request, body);
833+
};
834+
template.setInterceptors(Collections.singletonList(interceptor));
835+
836+
given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).willReturn(request);
837+
HttpHeaders requestHeaders = new HttpHeaders();
838+
given(request.getHeaders()).willReturn(requestHeaders);
839+
given(request.execute()).willReturn(response);
840+
given(errorHandler.hasError(response)).willReturn(false);
841+
HttpStatus status = HttpStatus.OK;
842+
given(response.getStatusCode()).willReturn(status);
843+
given(response.getStatusText()).willReturn(status.getReasonPhrase());
844+
845+
HttpHeaders entityHeaders = new HttpHeaders();
846+
entityHeaders.add("MyHeader", "MyEntityValue");
847+
HttpEntity<Void> entity = new HttpEntity<>(null, entityHeaders);
848+
template.exchange("http://example.com", HttpMethod.POST, entity, Void.class);
849+
assertThat(requestHeaders.get("MyHeader"), contains("MyEntityValue", "MyInterceptorValue"));
850+
851+
verify(response).close();
852+
}
853+
825854
}

0 commit comments

Comments
 (0)