diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml
index d7792363..7a4495f8 100644
--- a/spring-cloud-gateway-mvc/pom.xml
+++ b/spring-cloud-gateway-mvc/pom.xml
@@ -40,6 +40,11 @@
spring-boot-starter-actuator
test
+
+ org.apache.httpcomponents
+ httpclient
+ test
+
org.springframework.boot
spring-boot-configuration-processor
diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java
index f97b3477..84ab7a9f 100644
--- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java
+++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java
@@ -277,7 +277,7 @@ public class ProxyExchange {
}
public ResponseEntity get() {
- RequestEntity> requestEntity = headers((BodyBuilder) RequestEntity.get(uri)).build();
+ RequestEntity> requestEntity = headers((BodyBuilder) RequestEntity.get(uri)).body(body());
return exchange(requestEntity);
}
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/GetWithBodyRequestTests.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/GetWithBodyRequestTests.java
new file mode 100644
index 00000000..59c770c3
--- /dev/null
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/GetWithBodyRequestTests.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2016-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.springframework.cloud.gateway.mvc;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.cloud.gateway.mvc.GetWithBodyRequestTests.TestApplication.Foo;
+import org.springframework.cloud.gateway.mvc.config.ProxyExchangeArgumentResolver;
+import org.springframework.cloud.gateway.mvc.config.ProxyProperties;
+import org.springframework.cloud.gateway.mvc.http.GetWithBodyRequestClientHttpRequestFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.converter.ByteArrayHttpMessageConverter;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ContextConfiguration(classes = GetWithBodyRequestTests.TestApplication.class)
+public class GetWithBodyRequestTests {
+
+ @Autowired
+ private TestRestTemplate rest;
+
+ @Autowired
+ private TestApplication testApplication;
+
+ @LocalServerPort
+ private int port;
+
+ @Before
+ public void init() throws Exception {
+ testApplication.setHome(new URI("http://localhost:" + port));
+ rest.getRestTemplate().setRequestFactory(new GetWithBodyRequestClientHttpRequestFactory());
+ }
+
+ @Test
+ public void get() {
+ assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye");
+ }
+
+ @Test
+ public void getWithBodyRequest() {
+ final HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+
+ final Foo bodyRequest = new Foo("hello");
+ final HttpEntity entity = new HttpEntity<>(bodyRequest, headers);
+
+ final ResponseEntity response = rest.exchange("/proxy/get-with-body-request", HttpMethod.GET, entity,
+ Foo.class);
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).isInstanceOfSatisfying(Foo.class,
+ foo -> assertThat(foo.getName()).isEqualTo("hello world"));
+ }
+
+ @SpringBootApplication
+ static class TestApplication {
+
+ @Autowired
+ private ProxyController proxyController;
+
+ public void setHome(URI home) {
+ proxyController.setHome(home);
+ }
+
+ @Bean
+ public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(final ProxyProperties proxy) {
+ ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(
+ generateConfiguredRestTemplate());
+ resolver.setHeaders(proxy.convertHeaders());
+ resolver.setAutoForwardedHeaders(proxy.getAutoForward());
+ resolver.setSensitive(proxy.getSensitive());
+ return resolver;
+ }
+
+ private RestTemplate generateConfiguredRestTemplate() {
+ final RestTemplateBuilder builder = new RestTemplateBuilder();
+ final RestTemplate template = builder.build();
+
+ template.setRequestFactory(new GetWithBodyRequestClientHttpRequestFactory());
+ template.setErrorHandler(new NoOpResponseErrorHandler());
+ template.getMessageConverters().add(new ByteArrayHttpMessageConverter() {
+ @Override
+ public boolean supports(Class> clazz) {
+ return true;
+ }
+ });
+
+ return template;
+ }
+
+ @RestController
+ static class ProxyController {
+
+ private URI home;
+
+ public void setHome(URI home) {
+ this.home = home;
+ }
+
+ @GetMapping("/proxy/{id}")
+ public ResponseEntity> proxyFoos(@PathVariable Integer id, ProxyExchange> proxy) throws Exception {
+ return proxy.uri(home.toString() + "/foos/" + id).get();
+ }
+
+ @GetMapping("/proxy/get-with-body-request")
+ public ResponseEntity> proxyFooWithBody(@RequestBody Foo foo, ProxyExchange> proxy) throws Exception {
+ return proxy.uri(home.toString() + "/foo/get-with-body-request").get();
+ }
+
+ }
+
+ @RestController
+ static class TestController {
+
+ @GetMapping("/foos/{id}")
+ public Foo foo(@PathVariable Integer id, @RequestHeader HttpHeaders headers) {
+ String custom = headers.getFirst("X-Custom");
+ return new Foo(id == 1 ? "foo" : custom != null ? custom : "bye");
+ }
+
+ @GetMapping("/foo/get-with-body-request")
+ public Foo getWithBody(@RequestBody Foo foo) {
+ return new Foo(foo.getName() + " world");
+ }
+
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static class Foo {
+
+ private String name;
+
+ Foo() {
+ }
+
+ Foo(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ }
+
+ private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
+
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ }
+
+ }
+
+ }
+
+}
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java
index c2430e9c..74eb4c6e 100644
--- a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java
@@ -43,6 +43,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
@@ -75,6 +76,7 @@ public class ProductionConfigurationTests {
@Before
public void init() throws Exception {
application.setHome(new URI("http://localhost:" + port));
+ rest.getRestTemplate().setRequestFactory(new SimpleClientHttpRequestFactory());
}
@Test
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolverTest.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolverTest.java
index 9cf70aab..97a859c8 100644
--- a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolverTest.java
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolverTest.java
@@ -35,6 +35,7 @@ import org.springframework.cloud.gateway.mvc.ProxyExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@@ -61,6 +62,7 @@ public class ProxyExchangeArgumentResolverTest {
@Before
public void setUp() throws Exception {
application.setHome(new URI("http://localhost:" + port));
+ rest.getRestTemplate().setRequestFactory(new SimpleClientHttpRequestFactory());
}
@Test
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/AbstractBufferingClientHttpRequest.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/AbstractBufferingClientHttpRequest.java
new file mode 100644
index 00000000..64dfa458
--- /dev/null
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/AbstractBufferingClientHttpRequest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.springframework.cloud.gateway.mvc.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.client.AbstractClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+
+abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
+
+ private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream(1024);
+
+ protected OutputStream getBodyInternal(HttpHeaders headers) {
+ return bufferedOutput;
+ }
+
+ protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] body) throws IOException;
+
+ protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
+ final byte[] bytes = bufferedOutput.toByteArray();
+ if (headers.getContentLength() < 0L) {
+ headers.setContentLength(bytes.length);
+ }
+
+ final ClientHttpResponse response = executeInternal(headers, bytes);
+ bufferedOutput = new ByteArrayOutputStream(0);
+ return response;
+ }
+
+}
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/GetWithBodyRequestClientHttpRequestFactory.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/GetWithBodyRequestClientHttpRequestFactory.java
new file mode 100644
index 00000000..8dcee63b
--- /dev/null
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/GetWithBodyRequestClientHttpRequestFactory.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2016-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.springframework.cloud.gateway.mvc.http;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.Configurable;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.protocol.HttpContext;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpRequestFactory;
+
+public class GetWithBodyRequestClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
+
+ private final HttpClient httpClient;
+
+ public GetWithBodyRequestClientHttpRequestFactory() {
+ this.httpClient = HttpClients.createSystem();
+ }
+
+ @Override
+ public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) throws IOException {
+ final HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
+ final HttpContext context = HttpClientContext.create();
+
+ if (context.getAttribute("http.request-config") == null) {
+ RequestConfig config = null;
+ if (httpRequest instanceof Configurable) {
+ config = ((Configurable) httpRequest).getConfig();
+ }
+ if (config == null) {
+ config = createRequestConfig(httpClient);
+ }
+ if (config != null) {
+ context.setAttribute("http.request-config", config);
+ }
+ }
+
+ return new HttpComponentsClientHttpRequest(httpClient, httpRequest, context);
+ }
+
+ private HttpUriRequest createHttpUriRequest(final HttpMethod httpMethod, final URI uri) {
+ switch (httpMethod) {
+ case GET:
+ return new GetWithEntity(uri);
+ case HEAD:
+ return new HttpHead(uri);
+ case POST:
+ return new HttpPost(uri);
+ case PUT:
+ return new HttpPut(uri);
+ case PATCH:
+ return new HttpPatch(uri);
+ case DELETE:
+ return new HttpDelete(uri);
+ case OPTIONS:
+ return new HttpOptions(uri);
+ case TRACE:
+ return new HttpTrace(uri);
+ default:
+ throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
+ }
+ }
+
+ private RequestConfig createRequestConfig(final HttpClient client) {
+ if (client instanceof Configurable) {
+ return ((Configurable) client).getConfig();
+ }
+ return null;
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ if (httpClient instanceof Closeable) {
+ ((Closeable) httpClient).close();
+ }
+ }
+
+ public static class GetWithEntity extends HttpEntityEnclosingRequestBase {
+
+ public static final String METHOD_NAME = "GET";
+
+ public GetWithEntity(final URI uri) {
+ setURI(uri);
+ }
+
+ @Override
+ public String getMethod() {
+ return METHOD_NAME;
+ }
+
+ }
+
+}
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpRequest.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpRequest.java
new file mode 100644
index 00000000..d499d6f8
--- /dev/null
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpRequest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.springframework.cloud.gateway.mvc.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.protocol.HttpContext;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.StringUtils;
+
+final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpRequest {
+
+ private static final String COOKIE_HEADER_NAME = "Cookie";
+
+ private static final String CONTENT_LENGTH_HEADER_NAME = "Content-Length";
+
+ private static final String TRANSFER_ENCODING_HEADER_NAME = "Transfer-Encoding";
+
+ private final HttpClient httpClient;
+
+ private final HttpUriRequest httpRequest;
+
+ private final HttpContext httpContext;
+
+ HttpComponentsClientHttpRequest(final HttpClient httpClient, final HttpUriRequest httpRequest,
+ final HttpContext httpContext) {
+ this.httpClient = httpClient;
+ this.httpRequest = httpRequest;
+ this.httpContext = httpContext;
+ }
+
+ @Override
+ public String getMethodValue() {
+ return httpRequest.getMethod();
+ }
+
+ @Override
+ public URI getURI() {
+ return httpRequest.getURI();
+ }
+
+ @Override
+ protected ClientHttpResponse executeInternal(final HttpHeaders headers, final byte[] bufferedOutput)
+ throws IOException {
+ addHeaders(headers);
+ attachBodyRequest(bufferedOutput);
+
+ final HttpResponse response = httpClient.execute(httpRequest, httpContext);
+ return new HttpComponentsClientHttpResponse(response);
+ }
+
+ private void addHeaders(final HttpHeaders headers) {
+ headers.forEach((headerName, headerValues) -> {
+ if (COOKIE_HEADER_NAME.equalsIgnoreCase(headerName)) {
+ String headerValue = StringUtils.collectionToDelimitedString(headerValues, ": ");
+ httpRequest.addHeader(headerName, headerValue);
+ }
+ else if (!CONTENT_LENGTH_HEADER_NAME.equalsIgnoreCase(headerName)
+ && !TRANSFER_ENCODING_HEADER_NAME.equalsIgnoreCase(headerName)) {
+ final Iterator it = headerValues.iterator();
+ while (it.hasNext()) {
+ String value = it.next();
+ httpRequest.addHeader(headerName, value);
+ }
+ }
+ });
+ }
+
+ private void attachBodyRequest(final byte[] bufferedOutput) {
+ if (httpRequest instanceof HttpEntityEnclosingRequest) {
+ ((HttpEntityEnclosingRequest) httpRequest).setEntity(new ByteArrayEntity(bufferedOutput));
+ }
+ }
+
+}
diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpResponse.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpResponse.java
new file mode 100644
index 00000000..39f0e4b3
--- /dev/null
+++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/http/HttpComponentsClientHttpResponse.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.springframework.cloud.gateway.mvc.http;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.util.EntityUtils;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.client.AbstractClientHttpResponse;
+import org.springframework.lang.Nullable;
+import org.springframework.util.StreamUtils;
+
+final class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
+
+ private final HttpResponse response;
+
+ @Nullable
+ private HttpHeaders headers;
+
+ HttpComponentsClientHttpResponse(final HttpResponse response) {
+ this.response = response;
+ }
+
+ @Override
+ public int getRawStatusCode() throws IOException {
+ return response.getStatusLine().getStatusCode();
+ }
+
+ @Override
+ public String getStatusText() throws IOException {
+ return response.getStatusLine().getReasonPhrase();
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ if (headers == null) {
+ headers = new HttpHeaders();
+ final Header[] responseHeaders = response.getAllHeaders();
+ for (Header header : responseHeaders) {
+ headers.add(header.getName(), header.getValue());
+ }
+ }
+ return headers;
+ }
+
+ @Override
+ public InputStream getBody() throws IOException {
+ final HttpEntity entity = response.getEntity();
+ return entity != null ? entity.getContent() : StreamUtils.emptyInput();
+ }
+
+ @Override
+ public void close() {
+ try {
+ try {
+ EntityUtils.consume(response.getEntity());
+ }
+ finally {
+ if (response instanceof Closeable) {
+ ((Closeable) response).close();
+ }
+ }
+ }
+ catch (IOException ignored) {
+ }
+ }
+
+}