Remove AsyncRestTemplate and related types

This commit is contained in:
Rossen Stoyanchev 2021-10-08 17:59:03 +01:00
parent 932291b867
commit e3b48c23dd
40 changed files with 37 additions and 4813 deletions

View File

@ -1,55 +0,0 @@
/*
* Copyright 2002-2017 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.mock.http.client;
import java.io.IOException;
import java.net.URI;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
/**
* An extension of {@link MockClientHttpRequest} that also implements
* {@link org.springframework.http.client.AsyncClientHttpRequest} by wrapping the response in a
* {@link SettableListenableFuture}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 4.1
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
public class MockAsyncClientHttpRequest extends MockClientHttpRequest implements org.springframework.http.client.AsyncClientHttpRequest {
public MockAsyncClientHttpRequest() {
}
public MockAsyncClientHttpRequest(HttpMethod httpMethod, URI uri) {
super(httpMethod, uri);
}
@Override
public ListenableFuture<ClientHttpResponse> executeAsync() throws IOException {
SettableListenableFuture<ClientHttpResponse> future = new SettableListenableFuture<>();
future.set(execute());
return future;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -16,7 +16,6 @@
package org.springframework.test.web.client;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
@ -39,18 +38,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
/**
* A {@link ClientHttpRequestFactory} for requests executed via {@link MockMvc}.
*
* <p>As of 5.0 this class also implements
* {@link org.springframework.http.client.AsyncClientHttpRequestFactory
* AsyncClientHttpRequestFactory}. However note that
* {@link org.springframework.web.client.AsyncRestTemplate} and related classes
* have been deprecated at the same time.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
@SuppressWarnings("deprecation")
public class MockMvcClientHttpRequestFactory
implements ClientHttpRequestFactory, org.springframework.http.client.AsyncClientHttpRequestFactory {
public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory {
private final MockMvc mockMvc;
@ -65,22 +56,12 @@ public class MockMvcClientHttpRequestFactory
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return new MockClientHttpRequest(httpMethod, uri) {
@Override
public ClientHttpResponse executeInternal() throws IOException {
public ClientHttpResponse executeInternal() {
return getClientHttpResponse(httpMethod, uri, getHeaders(), getBodyAsBytes());
}
};
}
@Override
public org.springframework.http.client.AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod method) {
return new org.springframework.mock.http.client.MockAsyncClientHttpRequest(method, uri) {
@Override
protected ClientHttpResponse executeInternal() throws IOException {
return getClientHttpResponse(method, uri, getHeaders(), getBodyAsBytes());
}
};
}
private ClientHttpResponse getClientHttpResponse(
HttpMethod httpMethod, URI uri, HttpHeaders requestHeaders, byte[] requestBody) {

View File

@ -25,7 +25,7 @@ import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestGatewaySupport;
@ -64,7 +64,6 @@ import org.springframework.web.client.support.RestGatewaySupport;
* @author Rossen Stoyanchev
* @since 3.2
*/
@SuppressWarnings("deprecation")
public final class MockRestServiceServer {
private final RequestExpectationManager expectationManager;
@ -147,18 +146,6 @@ public final class MockRestServiceServer {
return new DefaultBuilder(restTemplate);
}
/**
* Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code AsyncRestTemplate}.
* @since 4.3
* @deprecated see deprecation notice on
* {@link org.springframework.web.client.AsyncRestTemplate} itself
*/
@Deprecated
public static MockRestServiceServerBuilder bindTo(org.springframework.web.client.AsyncRestTemplate asyncRestTemplate) {
return new DefaultBuilder(asyncRestTemplate);
}
/**
* Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code RestGatewaySupport}.
@ -179,18 +166,6 @@ public final class MockRestServiceServer {
return bindTo(restTemplate).build();
}
/**
* A shortcut for {@code bindTo(asyncRestTemplate).build()}.
* @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing
* @return the created mock server
* @deprecated see deprecation notice on
* {@link org.springframework.web.client.AsyncRestTemplate} itself
*/
@Deprecated
public static MockRestServiceServer createServer(org.springframework.web.client.AsyncRestTemplate asyncRestTemplate) {
return bindTo(asyncRestTemplate).build();
}
/**
* A shortcut for {@code bindTo(restGateway).build()}.
* @param restGateway the REST gateway to set up for mock testing
@ -226,8 +201,8 @@ public final class MockRestServiceServer {
/**
* Build the {@code MockRestServiceServer} and set up the underlying
* {@code RestTemplate} or {@code AsyncRestTemplate} with a
* {@link ClientHttpRequestFactory} that creates mock requests.
* {@code RestTemplate} with a {@link ClientHttpRequestFactory} that
* creates mock requests.
*/
MockRestServiceServer build();
@ -241,12 +216,8 @@ public final class MockRestServiceServer {
private static class DefaultBuilder implements MockRestServiceServerBuilder {
@Nullable
private final RestTemplate restTemplate;
@Nullable
private final org.springframework.web.client.AsyncRestTemplate asyncRestTemplate;
private boolean ignoreExpectOrder;
private boolean bufferContent;
@ -255,13 +226,6 @@ public final class MockRestServiceServer {
public DefaultBuilder(RestTemplate restTemplate) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
this.restTemplate = restTemplate;
this.asyncRestTemplate = null;
}
public DefaultBuilder(org.springframework.web.client.AsyncRestTemplate asyncRestTemplate) {
Assert.notNull(asyncRestTemplate, "AsyncRestTemplate must not be null");
this.restTemplate = null;
this.asyncRestTemplate = asyncRestTemplate;
}
@Override
@ -290,16 +254,11 @@ public final class MockRestServiceServer {
public MockRestServiceServer build(RequestExpectationManager manager) {
MockRestServiceServer server = new MockRestServiceServer(manager);
MockClientHttpRequestFactory factory = server.new MockClientHttpRequestFactory();
if (this.restTemplate != null) {
if (this.bufferContent) {
this.restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory));
}
else {
this.restTemplate.setRequestFactory(factory);
}
if (this.bufferContent) {
this.restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory));
}
if (this.asyncRestTemplate != null) {
this.asyncRestTemplate.setAsyncRequestFactory(factory);
else {
this.restTemplate.setRequestFactory(factory);
}
return server;
}
@ -310,28 +269,18 @@ public final class MockRestServiceServer {
* Mock ClientHttpRequestFactory that creates requests by iterating
* over the list of expected {@link DefaultRequestExpectation}'s.
*/
private class MockClientHttpRequestFactory implements ClientHttpRequestFactory,
org.springframework.http.client.AsyncClientHttpRequestFactory {
private class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return createRequestInternal(uri, httpMethod);
}
@Override
public org.springframework.http.client.AsyncClientHttpRequest createAsyncRequest(
URI uri, HttpMethod httpMethod) {
return createRequestInternal(uri, httpMethod);
}
private org.springframework.mock.http.client.MockAsyncClientHttpRequest createRequestInternal(
URI uri, HttpMethod httpMethod) {
private MockClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
Assert.notNull(uri, "'uri' must not be null");
Assert.notNull(httpMethod, "'httpMethod' must not be null");
return new org.springframework.mock.http.client.MockAsyncClientHttpRequest(httpMethod, uri) {
return new MockClientHttpRequest(httpMethod, uri) {
@Override
protected ClientHttpResponse executeInternal() throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -23,10 +23,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.ExpectedCount.twice;
@ -44,15 +44,15 @@ public class DefaultRequestExpectationTests {
@Test
public void match() throws Exception {
RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
expectation.match(createRequest(GET, "/foo"));
expectation.match(createRequest());
}
@Test
public void matchWithFailedExpectation() throws Exception {
public void matchWithFailedExpectation() {
RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
expectation.andExpect(method(POST));
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
expectation.match(createRequest(GET, "/foo")))
expectation.match(createRequest()))
.withMessageContaining("Unexpected HttpMethod expected:<POST> but was:<GET>");
}
@ -81,10 +81,9 @@ public class DefaultRequestExpectationTests {
}
@SuppressWarnings("deprecation")
private ClientHttpRequest createRequest(HttpMethod method, String url) {
private ClientHttpRequest createRequest() {
try {
return new org.springframework.mock.http.client.MockAsyncClientHttpRequest(method, new URI(url));
return new MockClientHttpRequest(HttpMethod.GET, new URI("/foo"));
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);

View File

@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -42,7 +43,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
*/
public class UnorderedRequestExpectationManagerTests {
private UnorderedRequestExpectationManager manager = new UnorderedRequestExpectationManager();
private final UnorderedRequestExpectationManager manager = new UnorderedRequestExpectationManager();
@Test
@ -57,7 +58,7 @@ public class UnorderedRequestExpectationManagerTests {
}
@Test
public void zeroExpectedRequests() throws Exception {
public void zeroExpectedRequests() {
this.manager.verify();
}
@ -108,19 +109,18 @@ public class UnorderedRequestExpectationManagerTests {
this.manager.validateRequest(createRequest(GET, "/bar"));
this.manager.validateRequest(createRequest(GET, "/foo"));
this.manager.validateRequest(createRequest(GET, "/foo"));
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
this.manager.verify())
.withMessageContaining("3 request(s) executed:\n" +
"GET /bar\n" +
"GET /foo\n" +
"GET /foo\n");
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(this.manager::verify)
.withMessageContaining("3 request(s) executed:\n" +
"GET /bar\n" +
"GET /foo\n" +
"GET /foo\n");
}
@SuppressWarnings("deprecation")
private ClientHttpRequest createRequest(HttpMethod method, String url) {
try {
return new org.springframework.mock.http.client.MockAsyncClientHttpRequest(method, new URI(url));
return new MockClientHttpRequest(method, new URI(url));
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -23,7 +23,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -31,7 +30,6 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@ -68,21 +66,12 @@ public class MockMvcClientHttpRequestFactoryTests {
}
@Test
public void test() throws Exception {
public void test() {
RestTemplate template = new RestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc));
String result = template.getForObject("/foo", String.class);
assertThat(result).isEqualTo("bar");
}
@Test
@SuppressWarnings("deprecation")
public void testAsyncTemplate() throws Exception {
org.springframework.web.client.AsyncRestTemplate template = new org.springframework.web.client.AsyncRestTemplate(
new MockMvcClientHttpRequestFactory(this.mockMvc));
ListenableFuture<ResponseEntity<String>> entity = template.getForEntity("/foo", String.class);
assertThat(entity.get().getBody()).isEqualTo("bar");
}
@EnableWebMvc
@Configuration

View File

@ -1,144 +0,0 @@
/*
* Copyright 2002-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.test.web.client.samples;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.Person;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.util.concurrent.ListenableFuture;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.ExpectedCount.manyTimes;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
/**
* Examples to demonstrate writing client-side REST tests with Spring MVC Test.
* While the tests in this class invoke the RestTemplate directly, in actual
* tests the RestTemplate may likely be invoked indirectly, i.e. through client
* code.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
@SuppressWarnings("deprecation")
public class SampleAsyncTests {
private final org.springframework.web.client.AsyncRestTemplate restTemplate = new org.springframework.web.client.AsyncRestTemplate();
private final MockRestServiceServer mockServer = MockRestServiceServer.createServer(this.restTemplate);
@Test
public void performGet() throws Exception {
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
ListenableFuture<ResponseEntity<Person>> ludwig =
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
// We are only validating the request. The response is mocked out.
// person.getName().equals("Ludwig van Beethoven")
// person.getDouble().equals(1.6035)
this.mockServer.verify();
}
@Test
public void performGetManyTimes() throws Exception {
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
this.mockServer.expect(manyTimes(), requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
ListenableFuture<ResponseEntity<Person>> ludwig =
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
// We are only validating the request. The response is mocked out.
// person.getName().equals("Ludwig van Beethoven")
// person.getDouble().equals(1.6035)
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
this.mockServer.verify();
}
@Test
public void performGetWithResponseBodyFromFile() throws Exception {
Resource responseBody = new ClassPathResource("ludwig.json", this.getClass());
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
ListenableFuture<ResponseEntity<Person>> ludwig =
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
// hotel.getId() == 42
// hotel.getName().equals("Holiday Inn")
this.mockServer.verify();
}
@Test
public void verify() {
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("1", MediaType.TEXT_PLAIN));
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("2", MediaType.TEXT_PLAIN));
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("4", MediaType.TEXT_PLAIN));
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("8", MediaType.TEXT_PLAIN));
@SuppressWarnings("unused")
ListenableFuture<ResponseEntity<String>> result = this.restTemplate.getForEntity("/number", String.class);
// result == "1"
result = this.restTemplate.getForEntity("/number", String.class);
// result == "2"
try {
this.mockServer.verify();
}
catch (AssertionError error) {
assertThat(error.getMessage().contains("2 unsatisfied expectation(s)")).as(error.getMessage()).isTrue();
}
}
}

View File

@ -1,85 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import java.io.OutputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Abstract base for {@link AsyncClientHttpRequest} that makes sure that headers and body
* are not written multiple times.
*
* @author Arjen Poutsma
* @since 4.0
* @deprecated as of Spring 5.0, in favor of {@link org.springframework.http.client.reactive.AbstractClientHttpRequest}
*/
@Deprecated
abstract class AbstractAsyncClientHttpRequest implements AsyncClientHttpRequest {
private final HttpHeaders headers = new HttpHeaders();
private boolean executed = false;
@Override
public final HttpHeaders getHeaders() {
return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
}
@Override
public final OutputStream getBody() throws IOException {
assertNotExecuted();
return getBodyInternal(this.headers);
}
@Override
public ListenableFuture<ClientHttpResponse> executeAsync() throws IOException {
assertNotExecuted();
ListenableFuture<ClientHttpResponse> result = executeInternal(this.headers);
this.executed = true;
return result;
}
/**
* Asserts that this request has not been {@linkplain #executeAsync() executed} yet.
* @throws IllegalStateException if this request has been executed
*/
protected void assertNotExecuted() {
Assert.state(!this.executed, "ClientHttpRequest already executed");
}
/**
* Abstract template method that returns the body.
* @param headers the HTTP headers
* @return the body output stream
*/
protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
/**
* Abstract template method that writes the given headers and content to the HTTP request.
* @param headers the HTTP headers
* @return the response object for the executed request
*/
protected abstract ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers)
throws IOException;
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Base implementation of {@link AsyncClientHttpRequest} that buffers output
* in a byte array before sending it over the wire.
*
* @author Arjen Poutsma
* @since 4.0
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
abstract class AbstractBufferingAsyncClientHttpRequest extends AbstractAsyncClientHttpRequest {
private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream(1024);
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
return this.bufferedOutput;
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers) throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
}
ListenableFuture<ClientHttpResponse> result = executeInternal(headers, bytes);
this.bufferedOutput = new ByteArrayOutputStream(0);
return result;
}
/**
* Abstract template method that writes the given headers and content to the HTTP request.
* @param headers the HTTP headers
* @param bufferedOutput the body content
* @return the response object for the executed request
*/
protected abstract ListenableFuture<ClientHttpResponse> executeInternal(
HttpHeaders headers, byte[] bufferedOutput) throws IOException;
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpRequest;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Represents a client-side asynchronous HTTP request. Created via an
* implementation of the {@link AsyncClientHttpRequestFactory}.
*
* <p>A {@code AsyncHttpRequest} can be {@linkplain #executeAsync() executed},
* getting a future {@link ClientHttpResponse} which can be read from.
*
* @author Arjen Poutsma
* @since 4.0
* @see AsyncClientHttpRequestFactory#createAsyncRequest
* @deprecated as of Spring 5.0, in favor of {@link org.springframework.web.reactive.function.client.ClientRequest}
*/
@Deprecated
public interface AsyncClientHttpRequest extends HttpRequest, HttpOutputMessage {
/**
* Execute this request asynchronously, resulting in a Future handle.
* {@link ClientHttpResponse} that can be read.
* @return the future response result of the execution
* @throws java.io.IOException in case of I/O errors
*/
ListenableFuture<ClientHttpResponse> executeAsync() throws IOException;
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import org.springframework.http.HttpRequest;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Represents the context of a client-side HTTP request execution.
*
* <p>Used to invoke the next interceptor in the interceptor chain, or -
* if the calling interceptor is last - execute the request itself.
*
* @author Jakub Narloch
* @author Rossen Stoyanchev
* @since 4.3
* @see AsyncClientHttpRequestInterceptor
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.web.reactive.function.client.ExchangeFilterFunction}
*/
@Deprecated
public interface AsyncClientHttpRequestExecution {
/**
* Resume the request execution by invoking the next interceptor in the chain
* or executing the request to the remote service.
* @param request the HTTP request, containing the HTTP method and headers
* @param body the body of the request
* @return a corresponding future handle
* @throws IOException in case of I/O errors
*/
ListenableFuture<ClientHttpResponse> executeAsync(HttpRequest request, byte[] body) throws IOException;
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import java.net.URI;
import org.springframework.http.HttpMethod;
/**
* Factory for {@link AsyncClientHttpRequest} objects.
* Requests are created by the {@link #createAsyncRequest(URI, HttpMethod)} method.
*
* @author Arjen Poutsma
* @since 4.0
* @deprecated as of Spring 5.0, in favor of {@link org.springframework.http.client.reactive.ClientHttpConnector}
*/
@Deprecated
public interface AsyncClientHttpRequestFactory {
/**
* Create a new asynchronous {@link AsyncClientHttpRequest} for the specified URI
* and HTTP method.
* <p>The returned request can be written to, and then executed by calling
* {@link AsyncClientHttpRequest#executeAsync()}.
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException;
}

View File

@ -1,73 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import org.springframework.http.HttpRequest;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Intercepts client-side HTTP requests. Implementations of this interface can be
* {@linkplain org.springframework.web.client.AsyncRestTemplate#setInterceptors registered}
* with the {@link org.springframework.web.client.AsyncRestTemplate} as to modify
* the outgoing {@link HttpRequest} and/or register to modify the incoming
* {@link ClientHttpResponse} with help of a
* {@link org.springframework.util.concurrent.ListenableFutureAdapter}.
*
* <p>The main entry point for interceptors is {@link #intercept}.
*
* @author Jakub Narloch
* @author Rossen Stoyanchev
* @since 4.3
* @see org.springframework.web.client.AsyncRestTemplate
* @see org.springframework.http.client.support.InterceptingAsyncHttpAccessor
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.web.reactive.function.client.ExchangeFilterFunction}
*/
@Deprecated
public interface AsyncClientHttpRequestInterceptor {
/**
* Intercept the given request, and return a response future. The given
* {@link AsyncClientHttpRequestExecution} allows the interceptor to pass on
* the request to the next entity in the chain.
* <p>An implementation might follow this pattern:
* <ol>
* <li>Examine the {@linkplain HttpRequest request} and body</li>
* <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper
* wrap} the request to filter HTTP attributes.</li>
* <li>Optionally modify the body of the request.</li>
* <li>One of the following:
* <ul>
* <li>execute the request through {@link ClientHttpRequestExecution}</li>
* <li>don't execute the request to block the execution altogether</li>
* </ul>
* <li>Optionally adapt the response to filter HTTP attributes with the help of
* {@link org.springframework.util.concurrent.ListenableFutureAdapter
* ListenableFutureAdapter}.</li>
* </ol>
* @param request the request, containing method, URI, and headers
* @param body the body of the request
* @param execution the request execution
* @return the response future
* @throws IOException in case of I/O errors
*/
ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body,
AsyncClientHttpRequestExecution execution) throws IOException;
}

View File

@ -1,172 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.Future;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpHeaders;
import org.springframework.util.concurrent.FailureCallback;
import org.springframework.util.concurrent.FutureAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.ListenableFutureCallbackRegistry;
import org.springframework.util.concurrent.SuccessCallback;
/**
* {@link ClientHttpRequest} implementation based on
* Apache HttpComponents HttpAsyncClient.
*
* <p>Created via the {@link HttpComponentsClientHttpRequestFactory}.
*
* @author Oleg Kalnichevski
* @author Arjen Poutsma
* @since 4.0
* @see HttpComponentsClientHttpRequestFactory#createRequest
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.HttpComponentsClientHttpConnector}
*/
@Deprecated
final class HttpComponentsAsyncClientHttpRequest extends AbstractBufferingAsyncClientHttpRequest {
private final HttpAsyncClient httpClient;
private final HttpUriRequest httpRequest;
private final HttpContext httpContext;
HttpComponentsAsyncClientHttpRequest(HttpAsyncClient client, HttpUriRequest request, HttpContext context) {
this.httpClient = client;
this.httpRequest = request;
this.httpContext = context;
}
@Override
public String getMethodValue() {
return this.httpRequest.getMethod();
}
@Override
public URI getURI() {
return this.httpRequest.getURI();
}
HttpContext getHttpContext() {
return this.httpContext;
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, byte[] bufferedOutput)
throws IOException {
HttpComponentsClientHttpRequest.addHeaders(this.httpRequest, headers);
if (this.httpRequest instanceof HttpEntityEnclosingRequest) {
HttpEntityEnclosingRequest entityEnclosingRequest = (HttpEntityEnclosingRequest) this.httpRequest;
HttpEntity requestEntity = new NByteArrayEntity(bufferedOutput);
entityEnclosingRequest.setEntity(requestEntity);
}
HttpResponseFutureCallback callback = new HttpResponseFutureCallback(this.httpRequest);
Future<HttpResponse> futureResponse = this.httpClient.execute(this.httpRequest, this.httpContext, callback);
return new ClientHttpResponseFuture(futureResponse, callback);
}
private static class HttpResponseFutureCallback implements FutureCallback<HttpResponse> {
private final HttpUriRequest request;
private final ListenableFutureCallbackRegistry<ClientHttpResponse> callbacks =
new ListenableFutureCallbackRegistry<>();
public HttpResponseFutureCallback(HttpUriRequest request) {
this.request = request;
}
public void addCallback(ListenableFutureCallback<? super ClientHttpResponse> callback) {
this.callbacks.addCallback(callback);
}
public void addSuccessCallback(SuccessCallback<? super ClientHttpResponse> callback) {
this.callbacks.addSuccessCallback(callback);
}
public void addFailureCallback(FailureCallback callback) {
this.callbacks.addFailureCallback(callback);
}
@Override
public void completed(HttpResponse result) {
this.callbacks.success(new HttpComponentsAsyncClientHttpResponse(result));
}
@Override
public void failed(Exception ex) {
this.callbacks.failure(ex);
}
@Override
public void cancelled() {
this.request.abort();
}
}
private static class ClientHttpResponseFuture extends FutureAdapter<ClientHttpResponse, HttpResponse>
implements ListenableFuture<ClientHttpResponse> {
private final HttpResponseFutureCallback callback;
public ClientHttpResponseFuture(Future<HttpResponse> response, HttpResponseFutureCallback callback) {
super(response);
this.callback = callback;
}
@Override
protected ClientHttpResponse adapt(HttpResponse response) {
return new HttpComponentsAsyncClientHttpResponse(response);
}
@Override
public void addCallback(ListenableFutureCallback<? super ClientHttpResponse> callback) {
this.callback.addCallback(callback);
}
@Override
public void addCallback(SuccessCallback<? super ClientHttpResponse> successCallback,
FailureCallback failureCallback) {
this.callback.addSuccessCallback(successCallback);
this.callback.addFailureCallback(failureCallback);
}
}
}

View File

@ -1,216 +0,0 @@
/*
* Copyright 2002-2018 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.http.client;
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.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.protocol.HttpContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
/**
* Asynchronous extension of the {@link HttpComponentsClientHttpRequestFactory}. Uses
* <a href="https://hc.apache.org/httpcomponents-asyncclient-dev/">Apache HttpComponents
* HttpAsyncClient 4.0</a> to create requests.
*
* @author Arjen Poutsma
* @author Stephane Nicoll
* @since 4.0
* @see HttpAsyncClient
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.HttpComponentsClientHttpConnector}
*/
@Deprecated
public class HttpComponentsAsyncClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory
implements AsyncClientHttpRequestFactory, InitializingBean {
private HttpAsyncClient asyncClient;
/**
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
* with a default {@link HttpAsyncClient} and {@link HttpClient}.
*/
public HttpComponentsAsyncClientHttpRequestFactory() {
super();
this.asyncClient = HttpAsyncClients.createSystem();
}
/**
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
* with the given {@link HttpAsyncClient} instance and a default {@link HttpClient}.
* @param asyncClient the HttpAsyncClient instance to use for this request factory
* @since 4.3.10
*/
public HttpComponentsAsyncClientHttpRequestFactory(HttpAsyncClient asyncClient) {
super();
this.asyncClient = asyncClient;
}
/**
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
* with the given {@link CloseableHttpAsyncClient} instance and a default {@link HttpClient}.
* @param asyncClient the CloseableHttpAsyncClient instance to use for this request factory
*/
public HttpComponentsAsyncClientHttpRequestFactory(CloseableHttpAsyncClient asyncClient) {
super();
this.asyncClient = asyncClient;
}
/**
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
* with the given {@link HttpClient} and {@link HttpAsyncClient} instances.
* @param httpClient the HttpClient instance to use for this request factory
* @param asyncClient the HttpAsyncClient instance to use for this request factory
* @since 4.3.10
*/
public HttpComponentsAsyncClientHttpRequestFactory(HttpClient httpClient, HttpAsyncClient asyncClient) {
super(httpClient);
this.asyncClient = asyncClient;
}
/**
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
* with the given {@link CloseableHttpClient} and {@link CloseableHttpAsyncClient} instances.
* @param httpClient the CloseableHttpClient instance to use for this request factory
* @param asyncClient the CloseableHttpAsyncClient instance to use for this request factory
*/
public HttpComponentsAsyncClientHttpRequestFactory(
CloseableHttpClient httpClient, CloseableHttpAsyncClient asyncClient) {
super(httpClient);
this.asyncClient = asyncClient;
}
/**
* Set the {@code HttpAsyncClient} used for
* {@linkplain #createAsyncRequest(URI, HttpMethod) synchronous execution}.
* @since 4.3.10
* @see #setHttpClient(HttpClient)
*/
public void setAsyncClient(HttpAsyncClient asyncClient) {
Assert.notNull(asyncClient, "HttpAsyncClient must not be null");
this.asyncClient = asyncClient;
}
/**
* Return the {@code HttpAsyncClient} used for
* {@linkplain #createAsyncRequest(URI, HttpMethod) synchronous execution}.
* @since 4.3.10
* @see #getHttpClient()
*/
public HttpAsyncClient getAsyncClient() {
return this.asyncClient;
}
/**
* Set the {@code CloseableHttpAsyncClient} used for
* {@linkplain #createAsyncRequest(URI, HttpMethod) asynchronous execution}.
* @deprecated as of 4.3.10, in favor of {@link #setAsyncClient(HttpAsyncClient)}
*/
@Deprecated
public void setHttpAsyncClient(CloseableHttpAsyncClient asyncClient) {
this.asyncClient = asyncClient;
}
/**
* Return the {@code CloseableHttpAsyncClient} used for
* {@linkplain #createAsyncRequest(URI, HttpMethod) asynchronous execution}.
* @deprecated as of 4.3.10, in favor of {@link #getAsyncClient()}
*/
@Deprecated
public CloseableHttpAsyncClient getHttpAsyncClient() {
Assert.state(this.asyncClient instanceof CloseableHttpAsyncClient,
"No CloseableHttpAsyncClient - use getAsyncClient() instead");
return (CloseableHttpAsyncClient) this.asyncClient;
}
@Override
public void afterPropertiesSet() {
startAsyncClient();
}
private HttpAsyncClient startAsyncClient() {
HttpAsyncClient client = getAsyncClient();
if (client instanceof CloseableHttpAsyncClient) {
@SuppressWarnings("resource")
CloseableHttpAsyncClient closeableAsyncClient = (CloseableHttpAsyncClient) client;
if (!closeableAsyncClient.isRunning()) {
closeableAsyncClient.start();
}
}
return client;
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpAsyncClient client = startAsyncClient();
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
postProcessHttpRequest(httpRequest);
HttpContext context = createHttpContext(httpMethod, uri);
if (context == null) {
context = HttpClientContext.create();
}
// Request configuration not set in the context
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
// Use request configuration given by the user, when available
RequestConfig config = null;
if (httpRequest instanceof Configurable) {
config = ((Configurable) httpRequest).getConfig();
}
if (config == null) {
config = createRequestConfig(client);
}
if (config != null) {
context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
}
}
return new HttpComponentsAsyncClientHttpRequest(client, httpRequest, context);
}
@Override
public void destroy() throws Exception {
try {
super.destroy();
}
finally {
HttpAsyncClient asyncClient = getAsyncClient();
if (asyncClient instanceof Closeable) {
((Closeable) asyncClient).close();
}
}
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
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.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
/**
* {@link ClientHttpResponse} implementation based on
* Apache HttpComponents HttpAsyncClient.
*
* <p>Created via the {@link HttpComponentsAsyncClientHttpRequest}.
*
* @author Oleg Kalnichevski
* @author Arjen Poutsma
* @since 4.0
* @see HttpComponentsAsyncClientHttpRequest#executeAsync()
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.HttpComponentsClientHttpConnector}
*/
@Deprecated
final class HttpComponentsAsyncClientHttpResponse extends AbstractClientHttpResponse {
private final HttpResponse httpResponse;
@Nullable
private HttpHeaders headers;
HttpComponentsAsyncClientHttpResponse(HttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
@Override
public int getRawStatusCode() throws IOException {
return this.httpResponse.getStatusLine().getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return this.httpResponse.getStatusLine().getReasonPhrase();
}
@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new HttpHeaders();
for (Header header : this.httpResponse.getAllHeaders()) {
this.headers.add(header.getName(), header.getValue());
}
}
return this.headers;
}
@Override
public InputStream getBody() throws IOException {
HttpEntity entity = this.httpResponse.getEntity();
return (entity != null ? entity.getContent() : StreamUtils.emptyInput());
}
@Override
public void close() {
// HTTP responses returned by async HTTP client are not bound to an
// active connection and do not have to deallocate any resources...
}
}

View File

@ -1,125 +0,0 @@
/*
* Copyright 2002-2018 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.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* An {@link AsyncClientHttpRequest} wrapper that enriches it proceeds the actual
* request execution with calling the registered interceptors.
*
* @author Jakub Narloch
* @author Rossen Stoyanchev
* @see InterceptingAsyncClientHttpRequestFactory
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
class InterceptingAsyncClientHttpRequest extends AbstractBufferingAsyncClientHttpRequest {
private AsyncClientHttpRequestFactory requestFactory;
private List<AsyncClientHttpRequestInterceptor> interceptors;
private URI uri;
private HttpMethod httpMethod;
/**
* Create new instance of {@link InterceptingAsyncClientHttpRequest}.
* @param requestFactory the async request factory
* @param interceptors the list of interceptors
* @param uri the request URI
* @param httpMethod the HTTP method
*/
public InterceptingAsyncClientHttpRequest(AsyncClientHttpRequestFactory requestFactory,
List<AsyncClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod httpMethod) {
this.requestFactory = requestFactory;
this.interceptors = interceptors;
this.uri = uri;
this.httpMethod = httpMethod;
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, byte[] body)
throws IOException {
return new AsyncRequestExecution().executeAsync(this, body);
}
@Override
public HttpMethod getMethod() {
return this.httpMethod;
}
@Override
public String getMethodValue() {
return this.httpMethod.name();
}
@Override
public URI getURI() {
return this.uri;
}
private class AsyncRequestExecution implements AsyncClientHttpRequestExecution {
private Iterator<AsyncClientHttpRequestInterceptor> iterator;
public AsyncRequestExecution() {
this.iterator = interceptors.iterator();
}
@Override
public ListenableFuture<ClientHttpResponse> executeAsync(HttpRequest request, byte[] body)
throws IOException {
if (this.iterator.hasNext()) {
AsyncClientHttpRequestInterceptor interceptor = this.iterator.next();
return interceptor.intercept(request, body, this);
}
else {
URI uri = request.getURI();
HttpMethod method = request.getMethod();
HttpHeaders headers = request.getHeaders();
Assert.state(method != null, "No standard HTTP method");
AsyncClientHttpRequest delegate = requestFactory.createAsyncRequest(uri, method);
delegate.getHeaders().putAll(headers);
if (body.length > 0) {
StreamUtils.copy(body, delegate.getBody());
}
return delegate.executeAsync();
}
}
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright 2002-2018 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.http.client;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
/**
* Wrapper for a {@link AsyncClientHttpRequestFactory} that has support for
* {@link AsyncClientHttpRequestInterceptor AsyncClientHttpRequestInterceptors}.
*
* @author Jakub Narloch
* @since 4.3
* @see InterceptingAsyncClientHttpRequest
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
public class InterceptingAsyncClientHttpRequestFactory implements AsyncClientHttpRequestFactory {
private AsyncClientHttpRequestFactory delegate;
private List<AsyncClientHttpRequestInterceptor> interceptors;
/**
* Create new instance of {@link InterceptingAsyncClientHttpRequestFactory}
* with delegated request factory and list of interceptors.
* @param delegate the request factory to delegate to
* @param interceptors the list of interceptors to use
*/
public InterceptingAsyncClientHttpRequestFactory(AsyncClientHttpRequestFactory delegate,
@Nullable List<AsyncClientHttpRequestInterceptor> interceptors) {
this.delegate = delegate;
this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod method) {
return new InterceptingAsyncClientHttpRequest(this.delegate, this.interceptors, uri, method);
}
}

View File

@ -1,187 +0,0 @@
/*
* Copyright 2002-2018 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.http.client;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpVersion;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
/**
* {@link ClientHttpRequest} implementation based on Netty 4.
*
* <p>Created via the {@link Netty4ClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 4.1.2
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.ReactorClientHttpConnector}
*/
@Deprecated
class Netty4ClientHttpRequest extends AbstractAsyncClientHttpRequest implements ClientHttpRequest {
private final Bootstrap bootstrap;
private final URI uri;
private final HttpMethod method;
private final ByteBufOutputStream body;
public Netty4ClientHttpRequest(Bootstrap bootstrap, URI uri, HttpMethod method) {
this.bootstrap = bootstrap;
this.uri = uri;
this.method = method;
this.body = new ByteBufOutputStream(Unpooled.buffer(1024));
}
@Override
public HttpMethod getMethod() {
return this.method;
}
@Override
public String getMethodValue() {
return this.method.name();
}
@Override
public URI getURI() {
return this.uri;
}
@Override
public ClientHttpResponse execute() throws IOException {
try {
return executeAsync().get();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted during request execution", ex);
}
catch (ExecutionException ex) {
if (ex.getCause() instanceof IOException) {
throw (IOException) ex.getCause();
}
else {
throw new IOException(ex.getMessage(), ex.getCause());
}
}
}
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
return this.body;
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(final HttpHeaders headers) throws IOException {
final SettableListenableFuture<ClientHttpResponse> responseFuture = new SettableListenableFuture<>();
ChannelFutureListener connectionListener = future -> {
if (future.isSuccess()) {
Channel channel = future.channel();
channel.pipeline().addLast(new RequestExecuteHandler(responseFuture));
FullHttpRequest nettyRequest = createFullHttpRequest(headers);
channel.writeAndFlush(nettyRequest);
}
else {
responseFuture.setException(future.cause());
}
};
this.bootstrap.connect(this.uri.getHost(), getPort(this.uri)).addListener(connectionListener);
return responseFuture;
}
private FullHttpRequest createFullHttpRequest(HttpHeaders headers) {
io.netty.handler.codec.http.HttpMethod nettyMethod =
io.netty.handler.codec.http.HttpMethod.valueOf(this.method.name());
String authority = this.uri.getRawAuthority();
String path = this.uri.toString().substring(this.uri.toString().indexOf(authority) + authority.length());
FullHttpRequest nettyRequest = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, nettyMethod, path, this.body.buffer());
nettyRequest.headers().set(HttpHeaders.HOST, this.uri.getHost() + ":" + getPort(this.uri));
nettyRequest.headers().set(HttpHeaders.CONNECTION, "close");
headers.forEach((headerName, headerValues) -> nettyRequest.headers().add(headerName, headerValues));
if (!nettyRequest.headers().contains(HttpHeaders.CONTENT_LENGTH) && this.body.buffer().readableBytes() > 0) {
nettyRequest.headers().set(HttpHeaders.CONTENT_LENGTH, this.body.buffer().readableBytes());
}
return nettyRequest;
}
private static int getPort(URI uri) {
int port = uri.getPort();
if (port == -1) {
if ("http".equalsIgnoreCase(uri.getScheme())) {
port = 80;
}
else if ("https".equalsIgnoreCase(uri.getScheme())) {
port = 443;
}
}
return port;
}
/**
* A SimpleChannelInboundHandler to update the given SettableListenableFuture.
*/
private static class RequestExecuteHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
private final SettableListenableFuture<ClientHttpResponse> responseFuture;
public RequestExecuteHandler(SettableListenableFuture<ClientHttpResponse> responseFuture) {
this.responseFuture = responseFuture;
}
@Override
protected void channelRead0(ChannelHandlerContext context, FullHttpResponse response) throws Exception {
this.responseFuture.set(new Netty4ClientHttpResponse(context, response));
}
@Override
public void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
this.responseFuture.setException(cause);
}
}
}

View File

@ -1,243 +0,0 @@
/*
* Copyright 2002-2018 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.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.SocketChannelConfig;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link org.springframework.http.client.ClientHttpRequestFactory} implementation
* that uses <a href="https://netty.io/">Netty 4</a> to create requests.
*
* <p>Allows to use a pre-configured {@link EventLoopGroup} instance: useful for
* sharing across multiple clients.
*
* <p>Note that this implementation consistently closes the HTTP connection on each
* request.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Brian Clozel
* @author Mark Paluch
* @since 4.1.2
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.ReactorClientHttpConnector}
*/
@Deprecated
public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
AsyncClientHttpRequestFactory, InitializingBean, DisposableBean {
/**
* The default maximum response size.
* @see #setMaxResponseSize(int)
*/
public static final int DEFAULT_MAX_RESPONSE_SIZE = 1024 * 1024 * 10;
private final EventLoopGroup eventLoopGroup;
private final boolean defaultEventLoopGroup;
private int maxResponseSize = DEFAULT_MAX_RESPONSE_SIZE;
@Nullable
private SslContext sslContext;
private int connectTimeout = -1;
private int readTimeout = -1;
@Nullable
private volatile Bootstrap bootstrap;
/**
* Create a new {@code Netty4ClientHttpRequestFactory} with a default
* {@link NioEventLoopGroup}.
*/
public Netty4ClientHttpRequestFactory() {
int ioWorkerCount = Runtime.getRuntime().availableProcessors() * 2;
this.eventLoopGroup = new NioEventLoopGroup(ioWorkerCount);
this.defaultEventLoopGroup = true;
}
/**
* Create a new {@code Netty4ClientHttpRequestFactory} with the given
* {@link EventLoopGroup}.
* <p><b>NOTE:</b> the given group will <strong>not</strong> be
* {@linkplain EventLoopGroup#shutdownGracefully() shutdown} by this factory;
* doing so becomes the responsibility of the caller.
*/
public Netty4ClientHttpRequestFactory(EventLoopGroup eventLoopGroup) {
Assert.notNull(eventLoopGroup, "EventLoopGroup must not be null");
this.eventLoopGroup = eventLoopGroup;
this.defaultEventLoopGroup = false;
}
/**
* Set the default maximum response size.
* <p>By default this is set to {@link #DEFAULT_MAX_RESPONSE_SIZE}.
* @since 4.1.5
* @see HttpObjectAggregator#HttpObjectAggregator(int)
*/
public void setMaxResponseSize(int maxResponseSize) {
this.maxResponseSize = maxResponseSize;
}
/**
* Set the SSL context. When configured it is used to create and insert an
* {@link io.netty.handler.ssl.SslHandler} in the channel pipeline.
* <p>A default client SslContext is configured if none has been provided.
*/
public void setSslContext(SslContext sslContext) {
this.sslContext = sslContext;
}
/**
* Set the underlying connect timeout (in milliseconds).
* A timeout value of 0 specifies an infinite timeout.
* @see ChannelConfig#setConnectTimeoutMillis(int)
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* Set the underlying URLConnection's read timeout (in milliseconds).
* A timeout value of 0 specifies an infinite timeout.
* @see ReadTimeoutHandler
*/
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
@Override
public void afterPropertiesSet() {
if (this.sslContext == null) {
this.sslContext = getDefaultClientSslContext();
}
}
private SslContext getDefaultClientSslContext() {
try {
return SslContextBuilder.forClient().build();
}
catch (SSLException ex) {
throw new IllegalStateException("Could not create default client SslContext", ex);
}
}
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
return createRequestInternal(uri, httpMethod);
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
return createRequestInternal(uri, httpMethod);
}
private Netty4ClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
return new Netty4ClientHttpRequest(getBootstrap(uri), uri, httpMethod);
}
private Bootstrap getBootstrap(URI uri) {
boolean isSecure = (uri.getPort() == 443 || "https".equalsIgnoreCase(uri.getScheme()));
if (isSecure) {
return buildBootstrap(uri, true);
}
else {
Bootstrap bootstrap = this.bootstrap;
if (bootstrap == null) {
bootstrap = buildBootstrap(uri, false);
this.bootstrap = bootstrap;
}
return bootstrap;
}
}
private Bootstrap buildBootstrap(URI uri, boolean isSecure) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(this.eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
configureChannel(channel.config());
ChannelPipeline pipeline = channel.pipeline();
if (isSecure) {
Assert.notNull(sslContext, "sslContext should not be null");
pipeline.addLast(sslContext.newHandler(channel.alloc(), uri.getHost(), uri.getPort()));
}
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(maxResponseSize));
if (readTimeout > 0) {
pipeline.addLast(new ReadTimeoutHandler(readTimeout,
TimeUnit.MILLISECONDS));
}
}
});
return bootstrap;
}
/**
* Template method for changing properties on the given {@link SocketChannelConfig}.
* <p>The default implementation sets the connect timeout based on the set property.
* @param config the channel configuration
*/
protected void configureChannel(SocketChannelConfig config) {
if (this.connectTimeout >= 0) {
config.setConnectTimeoutMillis(this.connectTimeout);
}
}
@Override
public void destroy() throws InterruptedException {
if (this.defaultEventLoopGroup) {
// Clean up the EventLoopGroup if we created it in the constructor
this.eventLoopGroup.shutdownGracefully().sync();
}
}
}

View File

@ -1,96 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link ClientHttpResponse} implementation based on Netty 4.
*
* @author Arjen Poutsma
* @since 4.1.2
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.http.client.reactive.ReactorClientHttpConnector}
*/
@Deprecated
class Netty4ClientHttpResponse extends AbstractClientHttpResponse {
private final ChannelHandlerContext context;
private final FullHttpResponse nettyResponse;
private final ByteBufInputStream body;
@Nullable
private volatile HttpHeaders headers;
public Netty4ClientHttpResponse(ChannelHandlerContext context, FullHttpResponse nettyResponse) {
Assert.notNull(context, "ChannelHandlerContext must not be null");
Assert.notNull(nettyResponse, "FullHttpResponse must not be null");
this.context = context;
this.nettyResponse = nettyResponse;
this.body = new ByteBufInputStream(this.nettyResponse.content());
this.nettyResponse.retain();
}
@Override
public int getRawStatusCode() throws IOException {
return this.nettyResponse.getStatus().code();
}
@Override
public String getStatusText() throws IOException {
return this.nettyResponse.getStatus().reasonPhrase();
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = this.headers;
if (headers == null) {
headers = new HttpHeaders();
for (Map.Entry<String, String> entry : this.nettyResponse.headers()) {
headers.add(entry.getKey(), entry.getValue());
}
this.headers = headers;
}
return headers;
}
@Override
public InputStream getBody() throws IOException {
return this.body;
}
@Override
public void close() {
this.nettyResponse.release();
this.context.close();
}
}

View File

@ -1,109 +0,0 @@
/*
* Copyright 2002-2017 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.http.client;
import java.io.IOException;
import java.net.URI;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
/**
* {@link AsyncClientHttpRequest} implementation based on OkHttp 3.x.
*
* <p>Created via the {@link OkHttp3ClientHttpRequestFactory}.
*
* @author Luciano Leggieri
* @author Arjen Poutsma
* @author Roy Clarkson
* @since 4.3
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
class OkHttp3AsyncClientHttpRequest extends AbstractBufferingAsyncClientHttpRequest {
private final OkHttpClient client;
private final URI uri;
private final HttpMethod method;
public OkHttp3AsyncClientHttpRequest(OkHttpClient client, URI uri, HttpMethod method) {
this.client = client;
this.uri = uri;
this.method = method;
}
@Override
public HttpMethod getMethod() {
return this.method;
}
@Override
public String getMethodValue() {
return this.method.name();
}
@Override
public URI getURI() {
return this.uri;
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, byte[] content)
throws IOException {
Request request = OkHttp3ClientHttpRequestFactory.buildRequest(headers, content, this.uri, this.method);
return new OkHttpListenableFuture(this.client.newCall(request));
}
private static class OkHttpListenableFuture extends SettableListenableFuture<ClientHttpResponse> {
private final Call call;
public OkHttpListenableFuture(Call call) {
this.call = call;
this.call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
set(new OkHttp3ClientHttpResponse(response));
}
@Override
public void onFailure(Call call, IOException ex) {
setException(ex);
}
});
}
@Override
protected void interruptTask() {
this.call.cancel();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -42,9 +42,7 @@ import org.springframework.util.StringUtils;
* @author Roy Clarkson
* @since 4.3
*/
@SuppressWarnings("deprecation")
public class OkHttp3ClientHttpRequestFactory
implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory, DisposableBean {
public class OkHttp3ClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
private OkHttpClient client;
@ -106,11 +104,6 @@ public class OkHttp3ClientHttpRequestFactory
return new OkHttp3ClientHttpRequest(this.client, uri, httpMethod);
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) {
return new OkHttp3AsyncClientHttpRequest(this.client, uri, httpMethod);
}
@Override
public void destroy() throws IOException {

View File

@ -1,99 +0,0 @@
/*
* Copyright 2002-2021 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.http.client;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* {@link org.springframework.http.client.ClientHttpRequest} implementation that uses
* standard JDK facilities to execute buffered requests. Created via the
* {@link org.springframework.http.client.SimpleClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @since 3.0
* @see org.springframework.http.client.SimpleClientHttpRequestFactory#createRequest
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
final class SimpleBufferingAsyncClientHttpRequest extends AbstractBufferingAsyncClientHttpRequest {
private final HttpURLConnection connection;
private final boolean outputStreaming;
private final AsyncListenableTaskExecutor taskExecutor;
SimpleBufferingAsyncClientHttpRequest(HttpURLConnection connection,
boolean outputStreaming, AsyncListenableTaskExecutor taskExecutor) {
this.connection = connection;
this.outputStreaming = outputStreaming;
this.taskExecutor = taskExecutor;
}
@Override
public String getMethodValue() {
return this.connection.getRequestMethod();
}
@Override
public URI getURI() {
try {
return this.connection.getURL().toURI();
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get HttpURLConnection URI: " + ex.getMessage(), ex);
}
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(
HttpHeaders headers, byte[] bufferedOutput) throws IOException {
return this.taskExecutor.submitListenable(() -> {
SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers);
// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
this.connection.connect();
if (this.connection.getDoOutput()) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
// Immediately trigger the request in a no-output scenario as well
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
});
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -23,10 +23,8 @@ import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link ClientHttpRequestFactory} implementation that uses standard JDK facilities.
@ -37,8 +35,7 @@ import org.springframework.util.Assert;
* @see java.net.HttpURLConnection
* @see HttpComponentsClientHttpRequestFactory
*/
@SuppressWarnings("deprecation")
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
private static final int DEFAULT_CHUNK_SIZE = 4096;
@ -56,9 +53,6 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory,
private boolean outputStreaming = true;
@Nullable
private AsyncListenableTaskExecutor taskExecutor;
/**
* Set the {@link Proxy} to use for this request factory.
@ -130,15 +124,6 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory,
this.outputStreaming = outputStreaming;
}
/**
* Set the task executor for this request factory. Setting this property is required
* for {@linkplain #createAsyncRequest(URI, HttpMethod) creating asynchronous requests}.
* @param taskExecutor the task executor
*/
public void setTaskExecutor(AsyncListenableTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
@ -153,27 +138,6 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory,
}
}
/**
* {@inheritDoc}
* <p>Setting the {@link #setTaskExecutor taskExecutor} property is required before calling this method.
*/
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
Assert.state(this.taskExecutor != null, "Asynchronous execution requires TaskExecutor to be set");
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingAsyncClientHttpRequest(
connection, this.outputStreaming, this.taskExecutor);
}
else {
return new SimpleStreamingAsyncClientHttpRequest(
connection, this.chunkSize, this.outputStreaming, this.taskExecutor);
}
}
/**
* Opens and returns a connection to the given URL.
* <p>The default implementation uses the given {@linkplain #setProxy(java.net.Proxy) proxy} -

View File

@ -1,125 +0,0 @@
/*
* Copyright 2002-2020 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.http.client;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* {@link org.springframework.http.client.ClientHttpRequest} implementation
* that uses standard Java facilities to execute streaming requests. Created
* via the {@link org.springframework.http.client.SimpleClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @since 3.0
* @see org.springframework.http.client.SimpleClientHttpRequestFactory#createRequest
* @see org.springframework.http.client.support.AsyncHttpAccessor
* @see org.springframework.web.client.AsyncRestTemplate
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
final class SimpleStreamingAsyncClientHttpRequest extends AbstractAsyncClientHttpRequest {
private final HttpURLConnection connection;
private final int chunkSize;
@Nullable
private OutputStream body;
private final boolean outputStreaming;
private final AsyncListenableTaskExecutor taskExecutor;
SimpleStreamingAsyncClientHttpRequest(HttpURLConnection connection, int chunkSize,
boolean outputStreaming, AsyncListenableTaskExecutor taskExecutor) {
this.connection = connection;
this.chunkSize = chunkSize;
this.outputStreaming = outputStreaming;
this.taskExecutor = taskExecutor;
}
@Override
public String getMethodValue() {
return this.connection.getRequestMethod();
}
@Override
public URI getURI() {
try {
return this.connection.getURL().toURI();
}
catch (URISyntaxException ex) {
throw new IllegalStateException(
"Could not get HttpURLConnection URI: " + ex.getMessage(), ex);
}
}
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
if (this.body == null) {
if (this.outputStreaming) {
long contentLength = headers.getContentLength();
if (contentLength >= 0) {
this.connection.setFixedLengthStreamingMode(contentLength);
}
else {
this.connection.setChunkedStreamingMode(this.chunkSize);
}
}
SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers);
this.connection.connect();
this.body = this.connection.getOutputStream();
}
return StreamUtils.nonClosing(this.body);
}
@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers) throws IOException {
return this.taskExecutor.submitListenable(() -> {
try {
if (this.body != null) {
this.body.close();
}
else {
SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers);
this.connection.connect();
// Immediately trigger the request in a no-output scenario as well
this.connection.getResponseCode();
}
}
catch (IOException ex) {
// ignore
}
return new SimpleClientHttpResponse(this.connection);
});
}
}

View File

@ -1,92 +0,0 @@
/*
* Copyright 2002-2018 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.http.client.support;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.logging.Log;
import org.springframework.http.HttpLogging;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Base class for {@link org.springframework.web.client.AsyncRestTemplate}
* and other HTTP accessing gateway helpers, defining common properties
* such as the {@link org.springframework.http.client.AsyncClientHttpRequestFactory}
* to operate on.
*
* <p>Not intended to be used directly. See
* {@link org.springframework.web.client.AsyncRestTemplate}.
*
* @author Arjen Poutsma
* @since 4.0
* @see org.springframework.web.client.AsyncRestTemplate
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
public class AsyncHttpAccessor {
/** Logger available to subclasses. */
protected final Log logger = HttpLogging.forLogName(getClass());
@Nullable
private org.springframework.http.client.AsyncClientHttpRequestFactory asyncRequestFactory;
/**
* Set the request factory that this accessor uses for obtaining {@link
* org.springframework.http.client.ClientHttpRequest HttpRequests}.
*/
public void setAsyncRequestFactory(
org.springframework.http.client.AsyncClientHttpRequestFactory asyncRequestFactory) {
Assert.notNull(asyncRequestFactory, "AsyncClientHttpRequestFactory must not be null");
this.asyncRequestFactory = asyncRequestFactory;
}
/**
* Return the request factory that this accessor uses for obtaining {@link
* org.springframework.http.client.ClientHttpRequest HttpRequests}.
*/
public org.springframework.http.client.AsyncClientHttpRequestFactory getAsyncRequestFactory() {
Assert.state(this.asyncRequestFactory != null, "No AsyncClientHttpRequestFactory set");
return this.asyncRequestFactory;
}
/**
* Create a new {@link org.springframework.http.client.AsyncClientHttpRequest} via this template's
* {@link org.springframework.http.client.AsyncClientHttpRequestFactory}.
* @param url the URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @return the created request
* @throws IOException in case of I/O errors
*/
protected org.springframework.http.client.AsyncClientHttpRequest createAsyncRequest(URI url, HttpMethod method)
throws IOException {
org.springframework.http.client.AsyncClientHttpRequest request =
getAsyncRequestFactory().createAsyncRequest(url, method);
if (logger.isDebugEnabled()) {
logger.debug("Created asynchronous " + method.name() + " request for \"" + url + "\"");
}
return request;
}
}

View File

@ -1,67 +0,0 @@
/*
* Copyright 2002-2017 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.http.client.support;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.CollectionUtils;
/**
* The HTTP accessor that extends the base {@link AsyncHttpAccessor} with
* request intercepting functionality.
*
* @author Jakub Narloch
* @author Rossen Stoyanchev
* @since 4.3
* @deprecated as of Spring 5.0, with no direct replacement
*/
@Deprecated
public abstract class InterceptingAsyncHttpAccessor extends AsyncHttpAccessor {
private List<org.springframework.http.client.AsyncClientHttpRequestInterceptor> interceptors =
new ArrayList<>();
/**
* Set the request interceptors that this accessor should use.
* @param interceptors the list of interceptors
*/
public void setInterceptors(List<org.springframework.http.client.AsyncClientHttpRequestInterceptor> interceptors) {
this.interceptors = interceptors;
}
/**
* Return the request interceptor that this accessor uses.
*/
public List<org.springframework.http.client.AsyncClientHttpRequestInterceptor> getInterceptors() {
return this.interceptors;
}
@Override
public org.springframework.http.client.AsyncClientHttpRequestFactory getAsyncRequestFactory() {
org.springframework.http.client.AsyncClientHttpRequestFactory delegate = super.getAsyncRequestFactory();
if (!CollectionUtils.isEmpty(getInterceptors())) {
return new org.springframework.http.client.InterceptingAsyncClientHttpRequestFactory(delegate, getInterceptors());
}
else {
return delegate;
}
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2002-2021 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.web.client;
import java.io.IOException;
/**
* Callback interface for code that operates on an
* {@link org.springframework.http.client.AsyncClientHttpRequest}. Allows to
* manipulate the request headers, and write to the request body.
*
* <p>Used internally by the {@link AsyncRestTemplate}, but also useful for
* application code.
*
* @author Arjen Poutsma
* @since 4.0
* @see org.springframework.web.client.AsyncRestTemplate#execute
* @deprecated as of Spring 5.0, in favor of
* {@link org.springframework.web.reactive.function.client.ExchangeFilterFunction}
*/
@FunctionalInterface
@Deprecated
public interface AsyncRequestCallback {
/**
* Gets called by {@link AsyncRestTemplate#execute} with an opened {@code ClientHttpRequest}.
* Does not need to care about closing the request or about handling errors:
* this will all be handled by the {@code RestTemplate}.
* @param request the active HTTP request
* @throws java.io.IOException in case of I/O errors
*/
void doWithRequest(org.springframework.http.client.AsyncClientHttpRequest request) throws IOException;
}

View File

@ -1,464 +0,0 @@
/*
* Copyright 2002-2017 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.web.client;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Interface specifying a basic set of asynchronous RESTful operations.
* Implemented by {@link AsyncRestTemplate}. Not often used directly, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Arjen Poutsma
* @since 4.0
* @see AsyncRestTemplate
* @see RestOperations
* @deprecated as of Spring 5.0, in favor of {@link org.springframework.web.reactive.function.client.WebClient}
*/
@Deprecated
public interface AsyncRestOperations {
/**
* Expose the synchronous Spring RestTemplate to allow synchronous invocation.
*/
RestOperations getRestOperations();
// GET
/**
* Asynchronously retrieve an entity by doing a GET on the specified URL.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Object... uriVariables) throws RestClientException;
/**
* Asynchronously retrieve a representation by doing a GET on the URI template.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously retrieve a representation by doing a GET on the URL.
* The response is converted and stored in an {@link ResponseEntity}.
* @param url the URL
* @param responseType the type of the return value
* @return the entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType)
throws RestClientException;
// HEAD
/**
* Asynchronously retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param uriVariables the variables to expand the template
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
ListenableFuture<HttpHeaders> headForHeaders(String url, Object... uriVariables)
throws RestClientException;
/**
* Asynchronously retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param uriVariables the map containing variables for the URI template
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
ListenableFuture<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables)
throws RestClientException;
/**
* Asynchronously retrieve all headers of the resource specified by the URL.
* @param url the URL
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
ListenableFuture<HttpHeaders> headForHeaders(URI url) throws RestClientException;
// POST
/**
* Create a new resource by POSTing the given object to the URI template, and
* asynchronously returns the value of the {@code Location} header. This header
* typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @param uriVariables the variables to expand the template
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
ListenableFuture<URI> postForLocation(String url, @Nullable HttpEntity<?> request, Object... uriVariables)
throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template, and
* asynchronously returns the value of the {@code Location} header. This header
* typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @param uriVariables the variables to expand the template
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
ListenableFuture<URI> postForLocation(String url, @Nullable HttpEntity<?> request, Map<String, ?> uriVariables)
throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL, and asynchronously
* returns the value of the {@code Location} header. This header typically indicates
* where the new resource is stored.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
ListenableFuture<URI> postForLocation(URI url, @Nullable HttpEntity<?> request) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and asynchronously returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @param uriVariables the variables to expand the template
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
Class<T> responseType, Object... uriVariables) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and asynchronously returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @param uriVariables the variables to expand the template
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL,
* and asynchronously returns the response as {@link ResponseEntity}.
* @param url the URL
* @param request the Object to be POSTed (may be {@code null})
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, @Nullable HttpEntity<?> request,
Class<T> responseType) throws RestClientException;
// PUT
/**
* Create or update a resource by PUTting the given object to the URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
* @param request the Object to be PUT (may be {@code null})
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
ListenableFuture<?> put(String url, @Nullable HttpEntity<?> request, Object... uriVariables)
throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URI template.
* <p>URI Template variables are expanded using the given map.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
* @param request the Object to be PUT (may be {@code null})
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
ListenableFuture<?> put(String url, @Nullable HttpEntity<?> request, Map<String, ?> uriVariables)
throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URL.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
* @param request the Object to be PUT (may be {@code null})
* @see HttpEntity
*/
ListenableFuture<?> put(URI url, @Nullable HttpEntity<?> request) throws RestClientException;
// DELETE
/**
* Asynchronously delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
* @param uriVariables the variables to expand in the template
*/
ListenableFuture<?> delete(String url, Object... uriVariables) throws RestClientException;
/**
* Asynchronously delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
* @param uriVariables the variables to expand in the template
*/
ListenableFuture<?> delete(String url, Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
*/
ListenableFuture<?> delete(URI url) throws RestClientException;
// OPTIONS
/**
* Asynchronously return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param uriVariables the variables to expand in the template
* @return the value of the allow header wrapped in a {@link Future}
*/
ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Object... uriVariables)
throws RestClientException;
/**
* Asynchronously return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param uriVariables the variables to expand in the template
* @return the value of the allow header wrapped in a {@link Future}
*/
ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVariables)
throws RestClientException;
/**
* Asynchronously return the value of the Allow header for the given URL.
* @param url the URL
* @return the value of the allow header wrapped in a {@link Future}
*/
ListenableFuture<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException;
// exchange
/**
* Asynchronously execute the HTTP method to the given URI template, writing the
* given request entity to the request, and returns the response as
* {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
* (may be {@code null})
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, writing the
* given request entity to the request, and returns the response as
* {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
* (may be {@code null})
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, writing the
* given request entity to the request, and returns the response as
* {@link ResponseEntity}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
* (may be {@code null})
* @param responseType the type of the return value
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType)
throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, writing the given
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type
* information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
* new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
*
* ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
* template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the
* request (may be {@code null})
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Object... uriVariables) throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, writing the given
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type
* information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
* new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
*
* ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
* template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
* (may be {@code null})
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, writing the given
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type
* information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
* new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
*
* ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
* template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
* </pre>
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
* (may be {@code null})
* @param responseType the type of the return value
* @return the response as entity wrapped in a {@link Future}
*/
<T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException;
// general execution
/**
* Asynchronously execute the HTTP method to the given URI template, preparing the
* request with the {@link AsyncRequestCallback}, and reading the response with a
* {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> ListenableFuture<T> execute(String url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor,
Object... uriVariables) throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URI template, preparing the
* request with the {@link AsyncRequestCallback}, and reading the response with a
* {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables map.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> ListenableFuture<T> execute(String url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor,
Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously execute the HTTP method to the given URL, preparing the request
* with the {@link AsyncRequestCallback}, and reading the response with a
* {@link ResponseExtractor}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> ListenableFuture<T> execute(URI url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException;
}

View File

@ -1,714 +0,0 @@
/*
* Copyright 2002-2018 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.web.client;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureAdapter;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriTemplateHandler;
/**
* <strong>Spring's central class for asynchronous client-side HTTP access.</strong>
* Exposes similar methods as {@link RestTemplate}, but returns {@link ListenableFuture}
* wrappers as opposed to concrete results.
*
* <p>The {@code AsyncRestTemplate} exposes a synchronous {@link RestTemplate} via the
* {@link #getRestOperations()} method and shares its {@linkplain #setErrorHandler error handler}
* and {@linkplain #setMessageConverters message converters} with that {@code RestTemplate}.
*
* <p><strong>Note:</strong> by default {@code AsyncRestTemplate} relies on
* standard JDK facilities to establish HTTP connections. You can switch to use
* a different HTTP library such as Apache HttpComponents, Netty, and OkHttp by
* using a constructor accepting an {@link org.springframework.http.client.AsyncClientHttpRequestFactory}.
*
* <p>For more information, please refer to the {@link RestTemplate} API documentation.
*
* @author Arjen Poutsma
* @since 4.0
* @see RestTemplate
* @deprecated as of Spring 5.0, in favor of {@link org.springframework.web.reactive.function.client.WebClient}
*/
@Deprecated
public class AsyncRestTemplate extends org.springframework.http.client.support.InterceptingAsyncHttpAccessor
implements AsyncRestOperations {
private final RestTemplate syncTemplate;
/**
* Create a new instance of the {@code AsyncRestTemplate} using default settings.
* <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination
* with a {@link SimpleAsyncTaskExecutor} for asynchronous execution.
*/
public AsyncRestTemplate() {
this(new SimpleAsyncTaskExecutor());
}
/**
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link AsyncTaskExecutor}.
* <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination
* with the given {@code AsyncTaskExecutor} for asynchronous execution.
*/
public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "AsyncTaskExecutor must not be null");
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setTaskExecutor(taskExecutor);
this.syncTemplate = new RestTemplate(requestFactory);
setAsyncRequestFactory(requestFactory);
}
/**
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link org.springframework.http.client.AsyncClientHttpRequestFactory}.
* <p>This constructor will cast the given asynchronous
* {@code AsyncClientHttpRequestFactory} to a {@link ClientHttpRequestFactory}. Since
* all implementations of {@code ClientHttpRequestFactory} provided in Spring also
* implement {@code AsyncClientHttpRequestFactory}, this should not result in a
* {@code ClassCastException}.
*/
public AsyncRestTemplate(org.springframework.http.client.AsyncClientHttpRequestFactory asyncRequestFactory) {
this(asyncRequestFactory, (ClientHttpRequestFactory) asyncRequestFactory);
}
/**
* Creates a new instance of the {@code AsyncRestTemplate} using the given
* asynchronous and synchronous request factories.
* @param asyncRequestFactory the asynchronous request factory
* @param syncRequestFactory the synchronous request factory
*/
public AsyncRestTemplate(org.springframework.http.client.AsyncClientHttpRequestFactory asyncRequestFactory,
ClientHttpRequestFactory syncRequestFactory) {
this(asyncRequestFactory, new RestTemplate(syncRequestFactory));
}
/**
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link org.springframework.http.client.AsyncClientHttpRequestFactory} and synchronous {@link RestTemplate}.
* @param requestFactory the asynchronous request factory to use
* @param restTemplate the synchronous template to use
*/
public AsyncRestTemplate(org.springframework.http.client.AsyncClientHttpRequestFactory requestFactory,
RestTemplate restTemplate) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
this.syncTemplate = restTemplate;
setAsyncRequestFactory(requestFactory);
}
/**
* Set the error handler.
* <p>By default, AsyncRestTemplate uses a
* {@link org.springframework.web.client.DefaultResponseErrorHandler}.
*/
public void setErrorHandler(ResponseErrorHandler errorHandler) {
this.syncTemplate.setErrorHandler(errorHandler);
}
/**
* Return the error handler.
*/
public ResponseErrorHandler getErrorHandler() {
return this.syncTemplate.getErrorHandler();
}
/**
* Configure default URI variable values. This is a shortcut for:
* <pre class="code">
* DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
* handler.setDefaultUriVariables(...);
*
* AsyncRestTemplate restTemplate = new AsyncRestTemplate();
* restTemplate.setUriTemplateHandler(handler);
* </pre>
* @param defaultUriVariables the default URI variable values
* @since 4.3
*/
@SuppressWarnings("deprecation")
public void setDefaultUriVariables(Map<String, ?> defaultUriVariables) {
UriTemplateHandler handler = this.syncTemplate.getUriTemplateHandler();
if (handler instanceof DefaultUriBuilderFactory) {
((DefaultUriBuilderFactory) handler).setDefaultUriVariables(defaultUriVariables);
}
else if (handler instanceof org.springframework.web.util.AbstractUriTemplateHandler) {
((org.springframework.web.util.AbstractUriTemplateHandler) handler)
.setDefaultUriVariables(defaultUriVariables);
}
else {
throw new IllegalArgumentException(
"This property is not supported with the configured UriTemplateHandler.");
}
}
/**
* This property has the same purpose as the corresponding property on the
* {@code RestTemplate}. For more details see
* {@link RestTemplate#setUriTemplateHandler}.
* @param handler the URI template handler to use
*/
public void setUriTemplateHandler(UriTemplateHandler handler) {
this.syncTemplate.setUriTemplateHandler(handler);
}
/**
* Return the configured URI template handler.
*/
public UriTemplateHandler getUriTemplateHandler() {
return this.syncTemplate.getUriTemplateHandler();
}
@Override
public RestOperations getRestOperations() {
return this.syncTemplate;
}
/**
* Set the message body converters to use.
* <p>These converters are used to convert from and to HTTP requests and responses.
*/
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.syncTemplate.setMessageConverters(messageConverters);
}
/**
* Return the message body converters.
*/
public List<HttpMessageConverter<?>> getMessageConverters() {
return this.syncTemplate.getMessageConverters();
}
// GET
@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType)
throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
// HEAD
@Override
public ListenableFuture<HttpHeaders> headForHeaders(String url, Object... uriVariables)
throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables);
}
@Override
public ListenableFuture<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables)
throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables);
}
@Override
public ListenableFuture<HttpHeaders> headForHeaders(URI url) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor);
}
// POST
@Override
public ListenableFuture<URI> postForLocation(String url, @Nullable HttpEntity<?> request, Object... uriVars)
throws RestClientException {
AsyncRequestCallback callback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor, uriVars);
return adaptToLocationHeader(future);
}
@Override
public ListenableFuture<URI> postForLocation(String url, @Nullable HttpEntity<?> request, Map<String, ?> uriVars)
throws RestClientException {
AsyncRequestCallback callback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor, uriVars);
return adaptToLocationHeader(future);
}
@Override
public ListenableFuture<URI> postForLocation(URI url, @Nullable HttpEntity<?> request)
throws RestClientException {
AsyncRequestCallback callback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor);
return adaptToLocationHeader(future);
}
private static ListenableFuture<URI> adaptToLocationHeader(ListenableFuture<HttpHeaders> future) {
return new ListenableFutureAdapter<URI, HttpHeaders>(future) {
@Override
@Nullable
protected URI adapt(HttpHeaders headers) throws ExecutionException {
return headers.getLocation();
}
};
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
Class<T> responseType, Object... uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url,
@Nullable HttpEntity<?> request, Class<T> responseType) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
// PUT
@Override
public ListenableFuture<?> put(String url, @Nullable HttpEntity<?> request, Object... uriVars)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVars);
}
@Override
public ListenableFuture<?> put(String url, @Nullable HttpEntity<?> request, Map<String, ?> uriVars)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVars);
}
@Override
public ListenableFuture<?> put(URI url, @Nullable HttpEntity<?> request) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null);
}
// DELETE
@Override
public ListenableFuture<?> delete(String url, Object... uriVariables) throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, uriVariables);
}
@Override
public ListenableFuture<?> delete(String url, Map<String, ?> uriVariables) throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, uriVariables);
}
@Override
public ListenableFuture<?> delete(URI url) throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null);
}
// OPTIONS
@Override
public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Object... uriVars)
throws RestClientException {
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor, uriVars);
return adaptToAllowHeader(future);
}
@Override
public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVars)
throws RestClientException {
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor, uriVars);
return adaptToAllowHeader(future);
}
@Override
public ListenableFuture<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException {
ResponseExtractor<HttpHeaders> extractor = headersExtractor();
ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor);
return adaptToAllowHeader(future);
}
private static ListenableFuture<Set<HttpMethod>> adaptToAllowHeader(ListenableFuture<HttpHeaders> future) {
return new ListenableFutureAdapter<Set<HttpMethod>, HttpHeaders>(future) {
@Override
protected Set<HttpMethod> adapt(HttpHeaders headers) throws ExecutionException {
return headers.getAllow();
}
};
}
// exchange
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, method, requestCallback, responseExtractor);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Object... uriVariables) throws RestClientException {
Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException {
Type type = responseType.getType();
AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
return execute(url, method, requestCallback, responseExtractor);
}
// general execution
@Override
public <T> ListenableFuture<T> execute(String url, HttpMethod method, @Nullable AsyncRequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
@Override
public <T> ListenableFuture<T> execute(String url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor,
Map<String, ?> uriVariables) throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
@Override
public <T> ListenableFuture<T> execute(URI url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
return doExecute(url, method, requestCallback, responseExtractor);
}
/**
* Execute the given method on the provided URI. The
* {@link org.springframework.http.client.ClientHttpRequest}
* is processed using the {@link RequestCallback}; the response with
* the {@link ResponseExtractor}.
* @param url the fully-expanded URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @param requestCallback object that prepares the request (can be {@code null})
* @param responseExtractor object that extracts the return value from the response (can
* be {@code null})
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
protected <T> ListenableFuture<T> doExecute(URI url, HttpMethod method,
@Nullable AsyncRequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
try {
org.springframework.http.client.AsyncClientHttpRequest request = createAsyncRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
ListenableFuture<ClientHttpResponse> responseFuture = request.executeAsync();
return new ResponseExtractorFuture<>(method, url, responseFuture, responseExtractor);
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + url + "\":" + ex.getMessage(), ex);
}
}
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug("Async " + method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + ")");
}
catch (IOException ex) {
// ignore
}
}
}
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn("Async " + method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException ex) {
// ignore
}
}
getErrorHandler().handleError(url, method, response);
}
/**
* Returns a request callback implementation that prepares the request {@code Accept}
* headers based on the given response type and configured {@linkplain
* #getMessageConverters() message converters}.
*/
protected <T> AsyncRequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
return new AsyncRequestCallbackAdapter(this.syncTemplate.acceptHeaderRequestCallback(responseType));
}
/**
* Returns a request callback implementation that writes the given object to the
* request stream.
*/
protected <T> AsyncRequestCallback httpEntityCallback(@Nullable HttpEntity<T> requestBody) {
return new AsyncRequestCallbackAdapter(this.syncTemplate.httpEntityCallback(requestBody));
}
/**
* Returns a request callback implementation that writes the given object to the
* request stream.
*/
protected <T> AsyncRequestCallback httpEntityCallback(@Nullable HttpEntity<T> request, Type responseType) {
return new AsyncRequestCallbackAdapter(this.syncTemplate.httpEntityCallback(request, responseType));
}
/**
* Returns a response extractor for {@link ResponseEntity}.
*/
protected <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) {
return this.syncTemplate.responseEntityExtractor(responseType);
}
/**
* Returns a response extractor for {@link HttpHeaders}.
*/
protected ResponseExtractor<HttpHeaders> headersExtractor() {
return this.syncTemplate.headersExtractor();
}
/**
* Future returned from
* {@link #doExecute(URI, HttpMethod, AsyncRequestCallback, ResponseExtractor)}.
*/
private class ResponseExtractorFuture<T> extends ListenableFutureAdapter<T, ClientHttpResponse> {
private final HttpMethod method;
private final URI url;
@Nullable
private final ResponseExtractor<T> responseExtractor;
public ResponseExtractorFuture(HttpMethod method, URI url,
ListenableFuture<ClientHttpResponse> clientHttpResponseFuture,
@Nullable ResponseExtractor<T> responseExtractor) {
super(clientHttpResponseFuture);
this.method = method;
this.url = url;
this.responseExtractor = responseExtractor;
}
@Override
@Nullable
protected final T adapt(ClientHttpResponse response) throws ExecutionException {
try {
if (!getErrorHandler().hasError(response)) {
logResponseStatus(this.method, this.url, response);
}
else {
handleResponseError(this.method, this.url, response);
}
return convertResponse(response);
}
catch (Throwable ex) {
throw new ExecutionException(ex);
}
finally {
response.close();
}
}
@Nullable
protected T convertResponse(ClientHttpResponse response) throws IOException {
return (this.responseExtractor != null ? this.responseExtractor.extractData(response) : null);
}
}
/**
* Adapts a {@link RequestCallback} to the {@link AsyncRequestCallback} interface.
*/
private static class AsyncRequestCallbackAdapter implements AsyncRequestCallback {
private final RequestCallback adaptee;
/**
* Create a new {@code AsyncRequestCallbackAdapter} from the given
* {@link RequestCallback}.
* @param requestCallback the callback to base this adapter on
*/
public AsyncRequestCallbackAdapter(RequestCallback requestCallback) {
this.adaptee = requestCallback;
}
@Override
public void doWithRequest(final org.springframework.http.client.AsyncClientHttpRequest request)
throws IOException {
this.adaptee.doWithRequest(new ClientHttpRequest() {
@Override
public ClientHttpResponse execute() throws IOException {
throw new UnsupportedOperationException("execute not supported");
}
@Override
public OutputStream getBody() throws IOException {
return request.getBody();
}
@Override
@Nullable
public HttpMethod getMethod() {
return request.getMethod();
}
@Override
public String getMethodValue() {
return request.getMethodValue();
}
@Override
public URI getURI() {
return request.getURI();
}
@Override
public HttpHeaders getHeaders() {
return request.getHeaders();
}
});
}
}
}

View File

@ -1,203 +0,0 @@
/*
* Copyright 2002-2020 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.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.Future;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@SuppressWarnings("deprecation")
public abstract class AbstractAsyncHttpRequestFactoryTests extends AbstractMockWebServerTests {
protected AsyncClientHttpRequestFactory factory;
@BeforeEach
public final void createFactory() throws Exception {
this.factory = createRequestFactory();
if (this.factory instanceof InitializingBean) {
((InitializingBean) this.factory).afterPropertiesSet();
}
}
@AfterEach
public final void destroyFactory() throws Exception {
if (this.factory instanceof DisposableBean) {
((DisposableBean) this.factory).destroy();
}
}
protected abstract AsyncClientHttpRequestFactory createRequestFactory();
@Test
public void status() throws Exception {
URI uri = new URI(baseUrl + "/status/notfound");
AsyncClientHttpRequest request = this.factory.createAsyncRequest(uri, HttpMethod.GET);
assertThat(request.getMethod()).as("Invalid HTTP method").isEqualTo(HttpMethod.GET);
assertThat(request.getURI()).as("Invalid HTTP URI").isEqualTo(uri);
Future<ClientHttpResponse> futureResponse = request.executeAsync();
try (ClientHttpResponse response = futureResponse.get()) {
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.NOT_FOUND);
}
}
@Test
public void statusCallback() throws Exception {
URI uri = new URI(baseUrl + "/status/notfound");
AsyncClientHttpRequest request = this.factory.createAsyncRequest(uri, HttpMethod.GET);
assertThat(request.getMethod()).as("Invalid HTTP method").isEqualTo(HttpMethod.GET);
assertThat(request.getURI()).as("Invalid HTTP URI").isEqualTo(uri);
ListenableFuture<ClientHttpResponse> listenableFuture = request.executeAsync();
listenableFuture.addCallback(new ListenableFutureCallback<ClientHttpResponse>() {
@Override
public void onSuccess(ClientHttpResponse result) {
try {
assertThat(result.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.NOT_FOUND);
}
catch (IOException ex) {
throw new AssertionError(ex.getMessage(), ex);
}
}
@Override
public void onFailure(Throwable ex) {
throw new AssertionError(ex.getMessage(), ex);
}
});
try (ClientHttpResponse response = listenableFuture.get()) {
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.NOT_FOUND);
}
}
@Test
public void echo() throws Exception {
AsyncClientHttpRequest request = this.factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.PUT);
assertThat(request.getMethod()).as("Invalid HTTP method").isEqualTo(HttpMethod.PUT);
String headerName = "MyHeader";
String headerValue1 = "value1";
request.getHeaders().add(headerName, headerValue1);
String headerValue2 = "value2";
request.getHeaders().add(headerName, headerValue2);
final byte[] body = "Hello World".getBytes("UTF-8");
request.getHeaders().setContentLength(body.length);
if (request instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
streamingRequest.setBody(outputStream -> StreamUtils.copy(body, outputStream));
}
else {
StreamUtils.copy(body, request.getBody());
}
Future<ClientHttpResponse> futureResponse = request.executeAsync();
try ( ClientHttpResponse response = futureResponse.get()) {
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
assertThat(response.getHeaders().containsKey(headerName)).as("Header not found").isTrue();
assertThat(response.getHeaders().get(headerName)).as("Header value not found").isEqualTo(Arrays.asList(headerValue1, headerValue2));
byte[] result = FileCopyUtils.copyToByteArray(response.getBody());
assertThat(Arrays.equals(body, result)).as("Invalid body").isTrue();
}
}
@Test
public void multipleWrites() throws Exception {
AsyncClientHttpRequest request = this.factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
final byte[] body = "Hello World".getBytes("UTF-8");
if (request instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
streamingRequest.setBody(outputStream -> StreamUtils.copy(body, outputStream));
}
else {
StreamUtils.copy(body, request.getBody());
}
Future<ClientHttpResponse> futureResponse = request.executeAsync();
try (ClientHttpResponse response = futureResponse.get()) {
assertThat(response).isNotNull();
assertThatIllegalStateException().isThrownBy(() -> FileCopyUtils.copy(body, request.getBody()));
}
}
@Test
public void headersAfterExecute() throws Exception {
AsyncClientHttpRequest request = this.factory.createAsyncRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
request.getHeaders().add("MyHeader", "value");
byte[] body = "Hello World".getBytes("UTF-8");
FileCopyUtils.copy(body, request.getBody());
Future<ClientHttpResponse> futureResponse = request.executeAsync();
try (ClientHttpResponse response = futureResponse.get()) {
assertThat(response).isNotNull();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
request.getHeaders().add("MyHeader", "value"));
}
}
@Test
public void httpMethods() throws Exception {
assertHttpMethod("get", HttpMethod.GET);
assertHttpMethod("head", HttpMethod.HEAD);
assertHttpMethod("post", HttpMethod.POST);
assertHttpMethod("put", HttpMethod.PUT);
assertHttpMethod("options", HttpMethod.OPTIONS);
assertHttpMethod("delete", HttpMethod.DELETE);
}
protected void assertHttpMethod(String path, HttpMethod method) throws Exception {
AsyncClientHttpRequest request = this.factory.createAsyncRequest(new URI(baseUrl + "/methods/" + path), method);
if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH) {
// requires a body
request.getBody().write(32);
}
Future<ClientHttpResponse> futureResponse = request.executeAsync();
try (ClientHttpResponse response = futureResponse.get()) {
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK);
assertThat(request.getMethod().name()).as("Invalid method").isEqualTo(path.toUpperCase(Locale.ENGLISH));
}
}
@Test
public void cancel() throws Exception {
URI uri = new URI(baseUrl + "/status/notfound");
AsyncClientHttpRequest request = this.factory.createAsyncRequest(uri, HttpMethod.GET);
Future<ClientHttpResponse> futureResponse = request.executeAsync();
futureResponse.cancel(true);
assertThat(futureResponse.isCancelled()).isTrue();
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2002-2013 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.http.client;
import java.net.ProtocolException;
import org.junit.jupiter.api.Test;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.http.HttpMethod;
public class BufferedSimpleAsyncHttpRequestFactoryTests extends AbstractAsyncHttpRequestFactoryTests {
@SuppressWarnings("deprecation")
@Override
protected AsyncClientHttpRequestFactory createRequestFactory() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
requestFactory.setTaskExecutor(taskExecutor);
return requestFactory;
}
@Override
@Test
public void httpMethods() throws Exception {
super.httpMethods();
try {
assertHttpMethod("patch", HttpMethod.PATCH);
}
catch (ProtocolException ex) {
// Currently HttpURLConnection does not support HTTP PATCH
}
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 2002-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.http.client;
import java.net.URI;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
* @author Stephane Nicoll
*/
@SuppressWarnings("deprecation")
public class HttpComponentsAsyncClientHttpRequestFactoryTests extends AbstractAsyncHttpRequestFactoryTests {
@Override
protected AsyncClientHttpRequestFactory createRequestFactory() {
return new HttpComponentsAsyncClientHttpRequestFactory();
}
@Override
@Test
public void httpMethods() throws Exception {
super.httpMethods();
assertHttpMethod("patch", HttpMethod.PATCH);
}
@Test
public void customHttpAsyncClientUsesItsDefault() throws Exception {
HttpComponentsAsyncClientHttpRequestFactory factory =
new HttpComponentsAsyncClientHttpRequestFactory();
URI uri = new URI(baseUrl + "/status/ok");
HttpComponentsAsyncClientHttpRequest request = (HttpComponentsAsyncClientHttpRequest)
factory.createAsyncRequest(uri, HttpMethod.GET);
assertThat(request.getHttpContext().getAttribute(HttpClientContext.REQUEST_CONFIG)).as("No custom config should be set with a custom HttpAsyncClient").isNull();
}
@Test
public void defaultSettingsOfHttpAsyncClientLostOnExecutorCustomization() throws Exception {
CloseableHttpAsyncClient client = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom().setConnectTimeout(1234).build())
.build();
HttpComponentsAsyncClientHttpRequestFactory factory = new HttpComponentsAsyncClientHttpRequestFactory(client);
URI uri = new URI(baseUrl + "/status/ok");
HttpComponentsAsyncClientHttpRequest request = (HttpComponentsAsyncClientHttpRequest)
factory.createAsyncRequest(uri, HttpMethod.GET);
assertThat(request.getHttpContext().getAttribute(HttpClientContext.REQUEST_CONFIG)).as("No custom config should be set with a custom HttpClient").isNull();
factory.setConnectionRequestTimeout(4567);
HttpComponentsAsyncClientHttpRequest request2 = (HttpComponentsAsyncClientHttpRequest)
factory.createAsyncRequest(uri, HttpMethod.GET);
Object requestConfigAttribute = request2.getHttpContext().getAttribute(HttpClientContext.REQUEST_CONFIG);
assertThat(requestConfigAttribute).isNotNull();
RequestConfig requestConfig = (RequestConfig) requestConfigAttribute;
assertThat(requestConfig.getConnectionRequestTimeout()).isEqualTo(4567);
// No way to access the request config of the HTTP client so no way to "merge" our customizations
assertThat(requestConfig.getConnectTimeout()).isEqualTo(-1);
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2002-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.http.client;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
/**
* @author Arjen Poutsma
*/
public class Netty4AsyncClientHttpRequestFactoryTests extends AbstractAsyncHttpRequestFactoryTests {
private static EventLoopGroup eventLoopGroup;
@BeforeAll
public static void createEventLoopGroup() {
eventLoopGroup = new NioEventLoopGroup();
}
@AfterAll
public static void shutdownEventLoopGroup() throws InterruptedException {
eventLoopGroup.shutdownGracefully().sync();
}
@SuppressWarnings("deprecation")
@Override
protected AsyncClientHttpRequestFactory createRequestFactory() {
return new Netty4ClientHttpRequestFactory(eventLoopGroup);
}
@Override
@Test
public void httpMethods() throws Exception {
super.httpMethods();
assertHttpMethod("patch", HttpMethod.PATCH);
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2002-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.http.client;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
/**
* @author Arjen Poutsma
*/
public class Netty4ClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
private static EventLoopGroup eventLoopGroup;
@BeforeAll
public static void createEventLoopGroup() {
eventLoopGroup = new NioEventLoopGroup();
}
@AfterAll
public static void shutdownEventLoopGroup() throws InterruptedException {
eventLoopGroup.shutdownGracefully().sync();
}
@Override
@SuppressWarnings("deprecation")
protected ClientHttpRequestFactory createRequestFactory() {
return new Netty4ClientHttpRequestFactory(eventLoopGroup);
}
@Override
@Test
public void httpMethods() throws Exception {
super.httpMethods();
assertHttpMethod("patch", HttpMethod.PATCH);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2002-2016 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.http.client;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
/**
* @author Roy Clarkson
*/
public class OkHttp3AsyncClientHttpRequestFactoryTests extends AbstractAsyncHttpRequestFactoryTests {
@SuppressWarnings("deprecation")
@Override
protected AsyncClientHttpRequestFactory createRequestFactory() {
return new OkHttp3ClientHttpRequestFactory();
}
@Override
@Test
public void httpMethods() throws Exception {
super.httpMethods();
assertHttpMethod("patch", HttpMethod.PATCH);
}
}

View File

@ -1,669 +0,0 @@
/*
* Copyright 2002-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.web.client;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.fail;
/**
* Integration tests for {@link AsyncRestTemplate}.
*
* <h3>Logging configuration for {@code MockWebServer}</h3>
*
* <p>In order for our log4j2 configuration to be used in an IDE, you must
* set the following system property before running any tests &mdash; for
* example, in <em>Run Configurations</em> in Eclipse.
*
* <pre class="code">
* -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager
* </pre>
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
*/
@SuppressWarnings("deprecation")
public class AsyncRestTemplateIntegrationTests extends AbstractMockWebServerTests {
private final AsyncRestTemplate template = new AsyncRestTemplate(
new org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory());
@Test
public void getEntity() throws Exception {
Future<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/{method}", String.class, "get");
ResponseEntity<String> entity = future.get();
assertThat(entity.getBody()).as("Invalid content").isEqualTo(helloWorld);
assertThat(entity.getHeaders().isEmpty()).as("No headers").isFalse();
assertThat(entity.getHeaders().getContentType()).as("Invalid content-type").isEqualTo(textContentType);
assertThat(entity.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
}
@Test
public void getEntityFromCompletable() throws Exception {
ListenableFuture<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/{method}", String.class, "get");
ResponseEntity<String> entity = future.completable().get();
assertThat(entity.getBody()).as("Invalid content").isEqualTo(helloWorld);
assertThat(entity.getHeaders().isEmpty()).as("No headers").isFalse();
assertThat(entity.getHeaders().getContentType()).as("Invalid content-type").isEqualTo(textContentType);
assertThat(entity.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
}
@Test
public void multipleFutureGets() throws Exception {
Future<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/{method}", String.class, "get");
future.get();
future.get();
}
@Test
public void getEntityCallback() throws Exception {
ListenableFuture<ResponseEntity<String>> futureEntity =
template.getForEntity(baseUrl + "/{method}", String.class, "get");
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> entity) {
assertThat(entity.getBody()).as("Invalid content").isEqualTo(helloWorld);
assertThat(entity.getHeaders().isEmpty()).as("No headers").isFalse();
assertThat(entity.getHeaders().getContentType()).as("Invalid content-type").isEqualTo(textContentType);
assertThat(entity.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(futureEntity);
}
@Test
public void getEntityCallbackWithLambdas() throws Exception {
ListenableFuture<ResponseEntity<String>> futureEntity =
template.getForEntity(baseUrl + "/{method}", String.class, "get");
futureEntity.addCallback(entity -> {
assertThat(entity.getBody()).as("Invalid content").isEqualTo(helloWorld);
assertThat(entity.getHeaders().isEmpty()).as("No headers").isFalse();
assertThat(entity.getHeaders().getContentType()).as("Invalid content-type").isEqualTo(textContentType);
assertThat(entity.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
}, ex -> fail(ex.getMessage()));
waitTillDone(futureEntity);
}
@Test
public void getNoResponse() throws Exception {
Future<ResponseEntity<String>> futureEntity = template.getForEntity(baseUrl + "/get/nothing", String.class);
ResponseEntity<String> entity = futureEntity.get();
assertThat(entity.getBody()).as("Invalid content").isNull();
}
@Test
public void getNoContentTypeHeader() throws Exception {
Future<ResponseEntity<byte[]>> futureEntity = template.getForEntity(baseUrl + "/get/nocontenttype", byte[].class);
ResponseEntity<byte[]> responseEntity = futureEntity.get();
assertThat(responseEntity.getBody()).as("Invalid content").isEqualTo(helloWorld.getBytes("UTF-8"));
}
@Test
public void getNoContent() throws Exception {
Future<ResponseEntity<String>> responseFuture = template.getForEntity(baseUrl + "/status/nocontent", String.class);
ResponseEntity<String> entity = responseFuture.get();
assertThat(entity.getStatusCode()).as("Invalid response code").isEqualTo(HttpStatus.NO_CONTENT);
assertThat(entity.getBody()).as("Invalid content").isNull();
}
@Test
public void getNotModified() throws Exception {
Future<ResponseEntity<String>> responseFuture = template.getForEntity(baseUrl + "/status/notmodified", String.class);
ResponseEntity<String> entity = responseFuture.get();
assertThat(entity.getStatusCode()).as("Invalid response code").isEqualTo(HttpStatus.NOT_MODIFIED);
assertThat(entity.getBody()).as("Invalid content").isNull();
}
@Test
public void headForHeaders() throws Exception {
Future<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get");
HttpHeaders headers = headersFuture.get();
assertThat(headers.containsKey("Content-Type")).as("No Content-Type header").isTrue();
}
@Test
public void headForHeadersCallback() throws Exception {
ListenableFuture<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get");
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@Override
public void onSuccess(HttpHeaders result) {
assertThat(result.containsKey("Content-Type")).as("No Content-Type header").isTrue();
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(headersFuture);
}
@Test
public void headForHeadersCallbackWithLambdas() throws Exception {
ListenableFuture<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get");
headersFuture.addCallback(result -> assertThat(result.containsKey("Content-Type")).as("No Content-Type header").isTrue(), ex -> fail(ex.getMessage()));
waitTillDone(headersFuture);
}
@Test
public void postForLocation() throws Exception {
HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(new MediaType("text", "plain", StandardCharsets.ISO_8859_1));
HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders);
Future<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post");
URI location = locationFuture.get();
assertThat(location).as("Invalid location").isEqualTo(new URI(baseUrl + "/post/1"));
}
@Test
public void postForLocationCallback() throws Exception {
HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(new MediaType("text", "plain", StandardCharsets.ISO_8859_1));
HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders);
final URI expected = new URI(baseUrl + "/post/1");
ListenableFuture<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post");
locationFuture.addCallback(new ListenableFutureCallback<URI>() {
@Override
public void onSuccess(URI result) {
assertThat(result).as("Invalid location").isEqualTo(expected);
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(locationFuture);
}
@Test
public void postForLocationCallbackWithLambdas() throws Exception {
HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.setContentType(new MediaType("text", "plain", StandardCharsets.ISO_8859_1));
HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders);
final URI expected = new URI(baseUrl + "/post/1");
ListenableFuture<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post");
locationFuture.addCallback(result -> assertThat(result).as("Invalid location").isEqualTo(expected),
ex -> fail(ex.getMessage()));
waitTillDone(locationFuture);
}
@Test
public void postForEntity() throws Exception {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
Future<ResponseEntity<String>> responseEntityFuture =
template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post");
ResponseEntity<String> responseEntity = responseEntityFuture.get();
assertThat(responseEntity.getBody()).as("Invalid content").isEqualTo(helloWorld);
}
@Test
public void postForEntityCallback() throws Exception {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
ListenableFuture<ResponseEntity<String>> responseEntityFuture =
template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post");
responseEntityFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> result) {
assertThat(result.getBody()).as("Invalid content").isEqualTo(helloWorld);
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(responseEntityFuture);
}
@Test
public void postForEntityCallbackWithLambdas() throws Exception {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
ListenableFuture<ResponseEntity<String>> responseEntityFuture =
template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post");
responseEntityFuture.addCallback(
result -> assertThat(result.getBody()).as("Invalid content").isEqualTo(helloWorld),
ex -> fail(ex.getMessage()));
waitTillDone(responseEntityFuture);
}
@Test
public void put() throws Exception {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
Future<?> responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity, "put");
responseEntityFuture.get();
}
@Test
public void putCallback() throws Exception {
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld);
ListenableFuture<?> responseEntityFuture = template.put(baseUrl + "/{method}", requestEntity, "put");
responseEntityFuture.addCallback(new ListenableFutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
assertThat(result).isNull();
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(responseEntityFuture);
}
@Test
public void delete() throws Exception {
Future<?> deletedFuture = template.delete(new URI(baseUrl + "/delete"));
deletedFuture.get();
}
@Test
public void deleteCallback() throws Exception {
ListenableFuture<?> deletedFuture = template.delete(new URI(baseUrl + "/delete"));
deletedFuture.addCallback(new ListenableFutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
assertThat(result).isNull();
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(deletedFuture);
}
@Test
public void deleteCallbackWithLambdas() throws Exception {
ListenableFuture<?> deletedFuture = template.delete(new URI(baseUrl + "/delete"));
deletedFuture.addCallback(result -> assertThat(result).isNull(), ex -> fail(ex.getMessage()));
waitTillDone(deletedFuture);
}
@Test
public void identicalExceptionThroughGetAndCallback() throws Exception {
final HttpClientErrorException[] callbackException = new HttpClientErrorException[1];
final CountDownLatch latch = new CountDownLatch(1);
ListenableFuture<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
future.addCallback(new ListenableFutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
fail("onSuccess not expected");
}
@Override
public void onFailure(Throwable ex) {
boolean condition = ex instanceof HttpClientErrorException;
assertThat(condition).isTrue();
callbackException[0] = (HttpClientErrorException) ex;
latch.countDown();
}
});
try {
future.get();
fail("Exception expected");
}
catch (ExecutionException ex) {
Throwable cause = ex.getCause();
boolean condition = cause instanceof HttpClientErrorException;
assertThat(condition).isTrue();
latch.await(5, TimeUnit.SECONDS);
assertThat(cause).isSameAs(callbackException[0]);
}
}
@Test
public void notFoundGet() throws Exception {
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> {
Future<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
future.get();
})
.withCauseInstanceOf(HttpClientErrorException.class)
.satisfies(ex -> {
HttpClientErrorException cause = (HttpClientErrorException) ex.getCause();
assertThat(cause.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(cause.getStatusText()).isNotNull();
assertThat(cause.getResponseBodyAsString()).isNotNull();
});
}
@Test
public void notFoundCallback() throws Exception {
ListenableFuture<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
future.addCallback(new ListenableFutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
fail("onSuccess not expected");
}
@Override
public void onFailure(Throwable t) {
boolean condition = t instanceof HttpClientErrorException;
assertThat(condition).isTrue();
HttpClientErrorException ex = (HttpClientErrorException) t;
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(ex.getStatusText()).isNotNull();
assertThat(ex.getResponseBodyAsString()).isNotNull();
}
});
waitTillDone(future);
}
@Test
public void notFoundCallbackWithLambdas() throws Exception {
ListenableFuture<?> future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null);
future.addCallback(result -> fail("onSuccess not expected"), ex -> {
boolean condition = ex instanceof HttpClientErrorException;
assertThat(condition).isTrue();
HttpClientErrorException hcex = (HttpClientErrorException) ex;
assertThat(hcex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(hcex.getStatusText()).isNotNull();
assertThat(hcex.getResponseBodyAsString()).isNotNull();
});
waitTillDone(future);
}
@Test
public void serverError() throws Exception {
try {
Future<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null);
future.get();
fail("HttpServerErrorException expected");
}
catch (ExecutionException ex) {
boolean condition = ex.getCause() instanceof HttpServerErrorException;
assertThat(condition).isTrue();
HttpServerErrorException cause = (HttpServerErrorException)ex.getCause();
assertThat(cause.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(cause.getStatusText()).isNotNull();
assertThat(cause.getResponseBodyAsString()).isNotNull();
}
}
@Test
public void serverErrorCallback() throws Exception {
ListenableFuture<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null);
future.addCallback(new ListenableFutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
fail("onSuccess not expected");
}
@Override
public void onFailure(Throwable ex) {
boolean condition = ex instanceof HttpServerErrorException;
assertThat(condition).isTrue();
HttpServerErrorException hsex = (HttpServerErrorException) ex;
assertThat(hsex.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(hsex.getStatusText()).isNotNull();
assertThat(hsex.getResponseBodyAsString()).isNotNull();
}
});
waitTillDone(future);
}
@Test
public void serverErrorCallbackWithLambdas() throws Exception {
ListenableFuture<Void> future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null);
future.addCallback(result -> fail("onSuccess not expected"), ex -> {
boolean condition = ex instanceof HttpServerErrorException;
assertThat(condition).isTrue();
HttpServerErrorException hsex = (HttpServerErrorException) ex;
assertThat(hsex.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(hsex.getStatusText()).isNotNull();
assertThat(hsex.getResponseBodyAsString()).isNotNull();
});
waitTillDone(future);
}
@Test
public void optionsForAllow() throws Exception {
Future<Set<HttpMethod>> allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
Set<HttpMethod> allowed = allowedFuture.get();
assertThat(allowed).as("Invalid response").isEqualTo(EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE));
}
@Test
public void optionsForAllowCallback() throws Exception {
ListenableFuture<Set<HttpMethod>> allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
allowedFuture.addCallback(new ListenableFutureCallback<Set<HttpMethod>>() {
@Override
public void onSuccess(Set<HttpMethod> result) {
assertThat(result).as("Invalid response").isEqualTo(EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS,
HttpMethod.HEAD, HttpMethod.TRACE));
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(allowedFuture);
}
@Test
public void optionsForAllowCallbackWithLambdas() throws Exception{
ListenableFuture<Set<HttpMethod>> allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get"));
allowedFuture.addCallback(result -> assertThat(result).as("Invalid response").isEqualTo(EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD,HttpMethod.TRACE)),
ex -> fail(ex.getMessage()));
waitTillDone(allowedFuture);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void exchangeGet() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
Future<ResponseEntity<String>> responseFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get");
ResponseEntity<String> response = responseFuture.get();
assertThat(response.getBody()).as("Invalid content").isEqualTo(helloWorld);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void exchangeGetCallback() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
ListenableFuture<ResponseEntity<String>> responseFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get");
responseFuture.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> result) {
assertThat(result.getBody()).as("Invalid content").isEqualTo(helloWorld);
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(responseFuture);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void exchangeGetCallbackWithLambdas() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
ListenableFuture<ResponseEntity<String>> responseFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get");
responseFuture.addCallback(result -> assertThat(result.getBody()).as("Invalid content").isEqualTo(helloWorld), ex -> fail(ex.getMessage()));
waitTillDone(responseFuture);
}
@Test
public void exchangePost() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders);
Future<ResponseEntity<Void>> resultFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post");
ResponseEntity<Void> result = resultFuture.get();
assertThat(result.getHeaders().getLocation()).as("Invalid location").isEqualTo(new URI(baseUrl + "/post/1"));
assertThat(result.hasBody()).isFalse();
}
@Test
public void exchangePostCallback() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders);
ListenableFuture<ResponseEntity<Void>> resultFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post");
final URI expected =new URI(baseUrl + "/post/1");
resultFuture.addCallback(new ListenableFutureCallback<ResponseEntity<Void>>() {
@Override
public void onSuccess(ResponseEntity<Void> result) {
assertThat(result.getHeaders().getLocation()).as("Invalid location").isEqualTo(expected);
assertThat(result.hasBody()).isFalse();
}
@Override
public void onFailure(Throwable ex) {
fail(ex.getMessage());
}
});
waitTillDone(resultFuture);
}
@Test
public void exchangePostCallbackWithLambdas() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyHeader", "MyValue");
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders);
ListenableFuture<ResponseEntity<Void>> resultFuture =
template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post");
final URI expected =new URI(baseUrl + "/post/1");
resultFuture.addCallback(result -> {
assertThat(result.getHeaders().getLocation()).as("Invalid location").isEqualTo(expected);
assertThat(result.hasBody()).isFalse();
}, ex -> fail(ex.getMessage()));
waitTillDone(resultFuture);
}
@Test
public void multipartFormData() throws Exception {
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("name 1", "value 1");
parts.add("name 2", "value 2+1");
parts.add("name 2", "value 2+2");
Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
parts.add("logo", logo);
HttpEntity<MultiValueMap<String, Object>> requestBody = new HttpEntity<>(parts);
Future<URI> future = template.postForLocation(baseUrl + "/multipartFormData", requestBody);
future.get();
}
@Test
public void getAndInterceptResponse() throws Exception {
RequestInterceptor interceptor = new RequestInterceptor();
template.setInterceptors(Collections.singletonList(interceptor));
ListenableFuture<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/get", String.class);
interceptor.latch.await(5, TimeUnit.SECONDS);
assertThat(interceptor.response).isNotNull();
assertThat(interceptor.response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(interceptor.exception).isNull();
assertThat(future.get().getBody()).isEqualTo(helloWorld);
}
@Test
public void getAndInterceptError() throws Exception {
RequestInterceptor interceptor = new RequestInterceptor();
template.setInterceptors(Collections.singletonList(interceptor));
template.getForEntity(baseUrl + "/status/notfound", String.class);
interceptor.latch.await(5, TimeUnit.SECONDS);
assertThat(interceptor.response).isNotNull();
assertThat(interceptor.response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(interceptor.exception).isNull();
}
private void waitTillDone(ListenableFuture<?> future) {
while (!future.isDone()) {
try {
Thread.sleep(5);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
private static class RequestInterceptor implements org.springframework.http.client.AsyncClientHttpRequestInterceptor {
private final CountDownLatch latch = new CountDownLatch(1);
private volatile ClientHttpResponse response;
private volatile Throwable exception;
@Override
public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body,
org.springframework.http.client.AsyncClientHttpRequestExecution execution) throws IOException {
ListenableFuture<ClientHttpResponse> future = execution.executeAsync(request, body);
future.addCallback(
resp -> {
response = resp;
this.latch.countDown();
},
ex -> {
exception = ex;
this.latch.countDown();
});
return future;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -92,7 +92,6 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests {
return Stream.of(
new SimpleClientHttpRequestFactory(),
new HttpComponentsClientHttpRequestFactory(),
new org.springframework.http.client.Netty4ClientHttpRequestFactory(),
new OkHttp3ClientHttpRequestFactory()
);
}

View File

@ -344,13 +344,6 @@ to `multipart/form-data` by the `FormHttpMessageConverter`. If the `MultiValueMa
If necessary the `Content-Type` may also be set explicitly.
[[rest-async-resttemplate]]
=== Using `AsyncRestTemplate` (Deprecated)
The `AsyncRestTemplate` is deprecated. For all use cases where you might consider using
`AsyncRestTemplate`, use the <<web-reactive.adoc#webflux-client, WebClient>> instead.
[[remoting]]
== Remoting and Web Services