Skip to content

Commit ed5740e

Browse files
authored
Merge branch 'master' into master
2 parents 21d31be + daed53a commit ed5740e

16 files changed

Lines changed: 1284 additions & 38 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### Version 11.9
2+
3+
* `OkHttpClient` now implements `AsyncClient`
4+
15
### Version 10.9
26

37
* Configurable to disable streaming mode for Default client by verils (#1182)

benchmark/pom.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,10 @@
2727
<name>Feign Benchmark (JMH)</name>
2828

2929
<properties>
30-
<jmh.version>1.34</jmh.version>
30+
<jmh.version>1.35</jmh.version>
3131
<rx.netty.version>0.5.3</rx.netty.version>
3232
<rx.java.version>1.3.8</rx.java.version>
33-
<netty.version>4.1.74.Final</netty.version>
34-
33+
<netty.version>4.1.77.Final</netty.version>
3534
<main.basedir>${project.basedir}/..</main.basedir>
3635
</properties>
3736

core/src/main/java/feign/Contract.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
132132

133133
if (parameterTypes[i] == URI.class) {
134134
data.urlIndex(i);
135-
} else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
135+
} else if (!isHttpAnnotation
136+
&& !Request.Options.class.isAssignableFrom(parameterTypes[i])) {
136137
if (data.isAlreadyProcessed(i)) {
137138
checkState(data.formParams().isEmpty() || data.bodyIndex() == null,
138139
"Body parameters cannot be used with form parameters.%s", data.warnings());

core/src/main/java/feign/MethodInfo.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ class MethodInfo {
3333
MethodInfo(Class<?> targetType, Method method) {
3434
this.configKey = Feign.configKey(targetType, method);
3535

36-
final Type type = method.getGenericReturnType();
36+
final Type type = Types.resolve(targetType, targetType, method.getGenericReturnType());
3737

38-
if (method.getReturnType() != CompletableFuture.class) {
39-
this.asyncReturnType = false;
40-
this.underlyingReturnType = type;
41-
} else {
38+
if (type instanceof ParameterizedType
39+
&& Types.getRawType(type).isAssignableFrom(CompletableFuture.class)) {
4240
this.asyncReturnType = true;
4341
this.underlyingReturnType = ((ParameterizedType) type).getActualTypeArguments()[0];
42+
} else {
43+
this.asyncReturnType = false;
44+
this.underlyingReturnType = type;
4445
}
4546
}
4647

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2012-2022 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign;
15+
16+
import java.lang.reflect.ParameterizedType;
17+
import java.lang.reflect.Type;
18+
import java.util.List;
19+
import java.util.concurrent.CompletableFuture;
20+
import org.junit.Test;
21+
import org.junit.experimental.runners.Enclosed;
22+
import org.junit.runner.RunWith;
23+
import static org.junit.Assert.*;
24+
25+
@RunWith(Enclosed.class)
26+
public class MethodInfoTest {
27+
28+
public static class AsyncClientTest {
29+
public interface AsyncClient {
30+
CompletableFuture<String> log();
31+
}
32+
33+
@Test
34+
public void testCompletableFutureOfString() throws Exception {
35+
MethodInfo mi = new MethodInfo(AsyncClient.class, AsyncClient.class.getMethod("log"));
36+
assertEquals("AsyncClient#log()", mi.configKey());
37+
assertTrue(mi.isAsyncReturnType());
38+
assertEquals(String.class, mi.underlyingReturnType());
39+
}
40+
}
41+
42+
public static class GenericAsyncClientTest {
43+
public interface GenericAsyncClient<T> {
44+
T log();
45+
}
46+
47+
public interface AsyncClient extends GenericAsyncClient<CompletableFuture<String>> {
48+
}
49+
50+
@Test
51+
public void testGenericCompletableFutureOfString() throws Exception {
52+
MethodInfo mi = new MethodInfo(AsyncClient.class, AsyncClient.class.getMethod("log"));
53+
assertEquals("AsyncClient#log()", mi.configKey());
54+
assertTrue(mi.isAsyncReturnType());
55+
assertEquals(String.class, mi.underlyingReturnType());
56+
}
57+
}
58+
59+
public static class SyncClientTest {
60+
public interface SyncClient {
61+
String log();
62+
}
63+
64+
@Test
65+
public void testString() throws Exception {
66+
MethodInfo mi = new MethodInfo(SyncClient.class, SyncClient.class.getMethod("log"));
67+
assertEquals("SyncClient#log()", mi.configKey());
68+
assertFalse(mi.isAsyncReturnType());
69+
assertEquals(String.class, mi.underlyingReturnType());
70+
}
71+
}
72+
73+
public static class GenericSyncClientTest {
74+
public interface GenericSyncClient<T> {
75+
T log();
76+
}
77+
78+
public interface SyncClient extends GenericSyncClient<List<String>> {
79+
}
80+
81+
public static class ListOfStrings implements ParameterizedType {
82+
@Override
83+
public Type[] getActualTypeArguments() {
84+
return new Type[] {String.class};
85+
}
86+
87+
@Override
88+
public Type getRawType() {
89+
return List.class;
90+
}
91+
92+
@Override
93+
public Type getOwnerType() {
94+
return null;
95+
}
96+
}
97+
98+
@Test
99+
public void testListOfStrings() throws Exception {
100+
MethodInfo mi = new MethodInfo(SyncClient.class, SyncClient.class.getMethod("log"));
101+
assertEquals("SyncClient#log()", mi.configKey());
102+
assertFalse(mi.isAsyncReturnType());
103+
assertTrue(Types.equals(new ListOfStrings(), mi.underlyingReturnType()));
104+
}
105+
}
106+
}

core/src/test/java/feign/OptionsTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,19 @@
2929
@SuppressWarnings("deprecation")
3030
public class OptionsTest {
3131

32+
static class ChildOptions extends Request.Options {
33+
public ChildOptions(int connectTimeoutMillis, int readTimeoutMillis) {
34+
super(connectTimeoutMillis, readTimeoutMillis);
35+
}
36+
}
37+
3238
interface OptionsInterface {
3339
@RequestLine("GET /")
3440
String get(Request.Options options);
3541

42+
@RequestLine("POST /")
43+
String getChildOptions(ChildOptions options);
44+
3645
@RequestLine("GET /")
3746
String get();
3847
}
@@ -66,4 +75,16 @@ public void normalResponseTest() {
6675

6776
assertThat(api.get(new Request.Options(1000, 4 * 1000))).isEqualTo("foo");
6877
}
78+
79+
@Test
80+
public void normalResponseForChildOptionsTest() {
81+
final MockWebServer server = new MockWebServer();
82+
server.enqueue(new MockResponse().setBody("foo").setBodyDelay(3, TimeUnit.SECONDS));
83+
84+
final OptionsInterface api = Feign.builder()
85+
.options(new ChildOptions(1000, 1000))
86+
.target(OptionsInterface.class, server.url("/").toString());
87+
88+
assertThat(api.getChildOptions(new ChildOptions(1000, 4 * 1000))).isEqualTo("foo");
89+
}
6990
}

hc5/src/main/java/feign/hc5/ApacheHttp5Client.java

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,40 @@
1515

1616
import static feign.Util.UTF_8;
1717
import static feign.Util.enumForName;
18+
import feign.Client;
19+
import feign.Request;
20+
import feign.Response;
21+
import feign.Util;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.InputStreamReader;
25+
import java.io.Reader;
26+
import java.net.URI;
27+
import java.net.URISyntaxException;
28+
import java.nio.charset.Charset;
29+
import java.util.ArrayList;
30+
import java.util.Collection;
31+
import java.util.HashMap;
32+
import java.util.List;
33+
import java.util.Map;
1834
import org.apache.hc.client5.http.classic.HttpClient;
1935
import org.apache.hc.client5.http.config.Configurable;
2036
import org.apache.hc.client5.http.config.RequestConfig;
2137
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2238
import org.apache.hc.client5.http.protocol.HttpClientContext;
23-
import org.apache.hc.core5.http.*;
39+
import org.apache.hc.core5.http.ClassicHttpRequest;
40+
import org.apache.hc.core5.http.ClassicHttpResponse;
41+
import org.apache.hc.core5.http.ContentType;
42+
import org.apache.hc.core5.http.Header;
43+
import org.apache.hc.core5.http.HttpEntity;
44+
import org.apache.hc.core5.http.HttpHost;
45+
import org.apache.hc.core5.http.NameValuePair;
2446
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
2547
import org.apache.hc.core5.http.io.entity.EntityUtils;
2648
import org.apache.hc.core5.http.io.entity.StringEntity;
2749
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
2850
import org.apache.hc.core5.net.URIBuilder;
2951
import org.apache.hc.core5.net.URLEncodedUtils;
30-
import java.io.*;
31-
import java.net.URI;
32-
import java.net.URISyntaxException;
33-
import java.nio.charset.Charset;
34-
import java.util.*;
35-
import feign.*;
3652

3753
/**
3854
* This module directs Feign's http requests to Apache's
@@ -97,8 +113,7 @@ ClassicHttpRequest toClassicHttpRequest(Request request, Request.Options options
97113
requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());
98114

99115
// request query params
100-
final List<NameValuePair> queryParams =
101-
URLEncodedUtils.parse(uri, requestBuilder.getCharset());
116+
final List<NameValuePair> queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset());
102117
for (final NameValuePair queryParam : queryParams) {
103118
requestBuilder.addParameter(queryParam);
104119
}
@@ -174,22 +189,22 @@ Response toFeignResponse(ClassicHttpResponse httpResponse, Request request) thro
174189

175190
final String reason = httpResponse.getReasonPhrase();
176191

177-
final Map<String, Collection<String>> headers = new HashMap<String, Collection<String>>();
192+
final Map<String, Collection<String>> headers = new HashMap<>();
178193
for (final Header header : httpResponse.getHeaders()) {
179194
final String name = header.getName();
180195
final String value = header.getValue();
181196

182197
Collection<String> headerValues = headers.get(name);
183198
if (headerValues == null) {
184-
headerValues = new ArrayList<String>();
199+
headerValues = new ArrayList<>();
185200
headers.put(name, headerValues);
186201
}
187202
headerValues.add(value);
188203
}
189204

190205
return Response.builder()
191-
.protocolVersion(enumForName(Request.ProtocolVersion.class,
192-
httpResponse.getVersion().format()))
206+
.protocolVersion(
207+
enumForName(Request.ProtocolVersion.class, httpResponse.getVersion().format()))
193208
.status(statusCode)
194209
.reason(reason)
195210
.headers(headers)
@@ -222,7 +237,6 @@ public InputStream asInputStream() throws IOException {
222237
return entity.getContent();
223238
}
224239

225-
@SuppressWarnings("deprecation")
226240
@Override
227241
public Reader asReader() throws IOException {
228242
return new InputStreamReader(asInputStream(), UTF_8);
@@ -236,7 +250,11 @@ public Reader asReader(Charset charset) throws IOException {
236250

237251
@Override
238252
public void close() throws IOException {
239-
EntityUtils.consume(entity);
253+
try {
254+
EntityUtils.consume(entity);
255+
} finally {
256+
httpResponse.close();
257+
}
240258
}
241259
};
242260
}

hc5/src/main/java/feign/hc5/AsyncApacheHttp5Client.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,7 @@ Response toFeignResponse(SimpleHttpResponse httpResponse, Request request) {
183183
.reason(reason)
184184
.headers(headers)
185185
.request(request)
186-
.body(httpResponse
187-
.getBodyBytes())
186+
.body(httpResponse.getBodyBytes())
188187
.build();
189188
}
190189

httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
import org.apache.http.StatusLine;
2121
import org.apache.http.client.HttpClient;
2222
import org.apache.http.client.config.RequestConfig;
23-
import org.apache.http.client.methods.Configurable;
24-
import org.apache.http.client.methods.HttpUriRequest;
25-
import org.apache.http.client.methods.RequestBuilder;
23+
import org.apache.http.client.methods.*;
2624
import org.apache.http.client.utils.URIBuilder;
2725
import org.apache.http.client.utils.URLEncodedUtils;
2826
import org.apache.http.entity.ByteArrayEntity;
@@ -233,6 +231,12 @@ public Reader asReader(Charset charset) throws IOException {
233231
@Override
234232
public void close() throws IOException {
235233
EntityUtils.consume(entity);
234+
try {
235+
EntityUtils.consume(entity);
236+
} finally {
237+
if (httpResponse instanceof CloseableHttpResponse)
238+
((CloseableHttpResponse) httpResponse).close();
239+
}
236240
}
237241
};
238242
}

okhttp/pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2012-2021 The Feign Authors
4+
Copyright 2012-2022 The Feign Authors
55
66
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
77
in compliance with the License. You may obtain a copy of the License at
@@ -54,5 +54,11 @@
5454
<artifactId>mockwebserver</artifactId>
5555
<scope>test</scope>
5656
</dependency>
57+
58+
<dependency>
59+
<groupId>com.google.code.gson</groupId>
60+
<artifactId>gson</artifactId>
61+
<scope>test</scope>
62+
</dependency>
5763
</dependencies>
5864
</project>

0 commit comments

Comments
 (0)