Skip to content

Commit 23ee09e

Browse files
meifanskdavisk6
authored andcommitted
Adds support for per request timeout options. Fixes OpenFeign#562 (OpenFeign#970)
* Add Options UT * Ignore Options when set bodyIndex
1 parent 2ee3f99 commit 23ee09e

File tree

3 files changed

+86
-6
lines changed

3 files changed

+86
-6
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
*/
1414
package feign;
1515

16-
import static feign.Util.checkState;
17-
import static feign.Util.emptyToNull;
18-
import feign.Request.HttpMethod;
1916
import java.lang.annotation.Annotation;
2017
import java.lang.reflect.Method;
2118
import java.lang.reflect.Modifier;
@@ -29,6 +26,9 @@
2926
import java.util.Map;
3027
import java.util.regex.Matcher;
3128
import java.util.regex.Pattern;
29+
import feign.Request.HttpMethod;
30+
import static feign.Util.checkState;
31+
import static feign.Util.emptyToNull;
3232

3333
/**
3434
* Defines what annotations and values are valid on interfaces.
@@ -111,7 +111,7 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
111111
}
112112
if (parameterTypes[i] == URI.class) {
113113
data.urlIndex(i);
114-
} else if (!isHttpAnnotation) {
114+
} else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
115115
checkState(data.formParams().isEmpty(),
116116
"Body parameters cannot be used with form parameters.");
117117
checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);

core/src/main/java/feign/SynchronousMethodHandler.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.IOException;
1717
import java.util.List;
1818
import java.util.concurrent.TimeUnit;
19+
import java.util.stream.Stream;
1920
import feign.InvocationHandlerFactory.MethodHandler;
2021
import feign.Request.Options;
2122
import feign.codec.DecodeException;
@@ -72,10 +73,11 @@ private SynchronousMethodHandler(Target<?> target, Client client, Retryer retrye
7273
@Override
7374
public Object invoke(Object[] argv) throws Throwable {
7475
RequestTemplate template = buildTemplateFromArgs.create(argv);
76+
Options options = findOptions(argv);
7577
Retryer retryer = this.retryer.clone();
7678
while (true) {
7779
try {
78-
return executeAndDecode(template);
80+
return executeAndDecode(template, options);
7981
} catch (RetryableException e) {
8082
try {
8183
retryer.continueOrPropagate(e);
@@ -95,7 +97,7 @@ public Object invoke(Object[] argv) throws Throwable {
9597
}
9698
}
9799

98-
Object executeAndDecode(RequestTemplate template) throws Throwable {
100+
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
99101
Request request = targetRequest(template);
100102

101103
if (logLevel != Logger.Level.NONE) {
@@ -181,6 +183,16 @@ Object decode(Response response) throws Throwable {
181183
}
182184
}
183185

186+
Options findOptions(Object[] argv) {
187+
if (argv == null || argv.length == 0) {
188+
return this.options;
189+
}
190+
return (Options) Stream.of(argv)
191+
.filter(o -> o instanceof Options)
192+
.findFirst()
193+
.orElse(this.options);
194+
}
195+
184196
static class Factory {
185197

186198
private final Client client;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright 2012-2019 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 org.hamcrest.CoreMatchers;
17+
import org.junit.Rule;
18+
import org.junit.Test;
19+
import org.junit.rules.ExpectedException;
20+
import java.net.SocketTimeoutException;
21+
import java.util.concurrent.TimeUnit;
22+
import okhttp3.mockwebserver.MockResponse;
23+
import okhttp3.mockwebserver.MockWebServer;
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* @author pengfei.zhao
28+
*/
29+
public class OptionsTest {
30+
31+
interface OptionsInterface {
32+
@RequestLine("GET /")
33+
String get(Request.Options options);
34+
35+
@RequestLine("GET /")
36+
String get();
37+
}
38+
39+
@Rule
40+
public final ExpectedException thrown = ExpectedException.none();
41+
42+
@Test
43+
public void socketTimeoutTest() {
44+
final MockWebServer server = new MockWebServer();
45+
server.enqueue(new MockResponse().setBody("foo").setBodyDelay(3, TimeUnit.SECONDS));
46+
47+
final OptionsInterface api = Feign.builder()
48+
.options(new Request.Options(1000, 1000))
49+
.target(OptionsInterface.class, server.url("/").toString());
50+
51+
thrown.expect(FeignException.class);
52+
thrown.expectCause(CoreMatchers.isA(SocketTimeoutException.class));
53+
54+
api.get();
55+
}
56+
57+
@Test
58+
public void normalResponseTest() {
59+
final MockWebServer server = new MockWebServer();
60+
server.enqueue(new MockResponse().setBody("foo").setBodyDelay(3, TimeUnit.SECONDS));
61+
62+
final OptionsInterface api = Feign.builder()
63+
.options(new Request.Options(1000, 1000))
64+
.target(OptionsInterface.class, server.url("/").toString());
65+
66+
assertThat(api.get(new Request.Options(1000, 4 * 1000))).isEqualTo("foo");
67+
}
68+
}

0 commit comments

Comments
 (0)