40 changed files with 37 additions and 4813 deletions
@ -1,55 +0,0 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -1,144 +0,0 @@
@@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,85 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,65 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,48 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,50 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,47 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,73 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,172 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,216 +0,0 @@
@@ -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(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,90 +0,0 @@
@@ -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...
|
||||
} |
||||
|
||||
} |
||||
@ -1,125 +0,0 @@
@@ -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(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,62 +0,0 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -1,187 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,243 +0,0 @@
@@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,96 +0,0 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -1,109 +0,0 @@
@@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,99 +0,0 @@
@@ -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); |
||||
}); |
||||
} |
||||
|
||||
} |
||||
@ -1,125 +0,0 @@
@@ -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); |
||||
}); |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,92 +0,0 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -1,67 +0,0 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,48 +0,0 @@
@@ -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; |
||||
|
||||
} |
||||
@ -1,464 +0,0 @@
@@ -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<List<MyBean>> myBean = |
||||
* new ParameterizedTypeReference<List<MyBean>>() {}; |
||||
* |
||||
* ResponseEntity<List<MyBean>> response = |
||||
* template.exchange("https://example.com",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<List<MyBean>> myBean = |
||||
* new ParameterizedTypeReference<List<MyBean>>() {}; |
||||
* |
||||
* ResponseEntity<List<MyBean>> response = |
||||
* template.exchange("https://example.com",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<List<MyBean>> myBean = |
||||
* new ParameterizedTypeReference<List<MyBean>>() {}; |
||||
* |
||||
* ResponseEntity<List<MyBean>> response = |
||||
* template.exchange("https://example.com",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; |
||||
|
||||
} |
||||
@ -1,714 +0,0 @@
@@ -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(); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,203 +0,0 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -1,50 +0,0 @@
@@ -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
|
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,88 +0,0 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -1,58 +0,0 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -1,58 +0,0 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -1,41 +0,0 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -1,669 +0,0 @@
@@ -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 — 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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue