Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/main/java/feign/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ HttpURLConnection convertAndSend(Request request, Options options) throws IOExce
connection.setConnectTimeout(options.connectTimeoutMillis());
connection.setReadTimeout(options.readTimeoutMillis());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(true);
connection.setInstanceFollowRedirects(options.isFollowRedirects());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this impact other clients too?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other clients would need to be updated to use the option.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the option default to true, so the previous clients will keep the previous behavior.

You have to explicitely set the option to false to have the new behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@velo I have added implementations for Ribbon, OkHttp and LbClient as well (since unit tests of Ribbon use it too).

connection.setRequestMethod(request.method());

Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
Expand Down
19 changes: 18 additions & 1 deletion core/src/main/java/feign/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package feign;

import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Map;
Expand Down Expand Up @@ -103,10 +104,16 @@ public static class Options {

private final int connectTimeoutMillis;
private final int readTimeoutMillis;
private final boolean followRedirects;

public Options(int connectTimeoutMillis, int readTimeoutMillis) {
public Options(int connectTimeoutMillis, int readTimeoutMillis, boolean followRedirects) {
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;
this.followRedirects = followRedirects;
}

public Options(int connectTimeoutMillis, int readTimeoutMillis){
this(connectTimeoutMillis, readTimeoutMillis, true);
}

public Options() {
Expand All @@ -130,5 +137,15 @@ public int connectTimeoutMillis() {
public int readTimeoutMillis() {
return readTimeoutMillis;
}


/**
* Defaults to true. {@code false} tells the client to not follow the redirections.
*
* @see HttpURLConnection#getFollowRedirects()
*/
public boolean isFollowRedirects() {
return followRedirects;
}
}
}
30 changes: 28 additions & 2 deletions core/src/test/java/feign/FeignBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -79,6 +80,31 @@ public void testDecode404() throws Exception {
}
}



@Test public void testNoFollowRedirect() {
server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location","/"));

String url = "http://localhost:" + server.getPort();
TestInterface noFollowApi = Feign.builder()
.options(new Request.Options(100, 600, false))
.target(TestInterface.class, url);

Response response = noFollowApi.defaultMethodPassthrough();
assertThat(response.status()).isEqualTo(302);
assertThat(response.headers().getOrDefault("Location", null))
.isNotNull()
.isEqualTo(Collections.singletonList("/"));

server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location","/"));
server.enqueue(new MockResponse().setResponseCode(200));
TestInterface defaultApi = Feign.builder()
.options(new Request.Options(100, 600, true))
.target(TestInterface.class, url);
assertThat(defaultApi.defaultMethodPassthrough().status()).isEqualTo(200);
}


@Test
public void testUrlPathConcatUrlTrailingSlash() throws Exception {
server.enqueue(new MockResponse().setBody("response data"));
Expand Down Expand Up @@ -208,7 +234,7 @@ public InvocationHandler create(Target target, Map<Method, MethodHandler> dispat
assertThat(server.takeRequest())
.hasBody("request data");
}

@Test
public void testSlashIsEncodedInPathParams() throws Exception {
server.enqueue(new MockResponse().setBody("response data"));
Expand Down Expand Up @@ -318,7 +344,7 @@ interface TestInterface {

@RequestLine("POST /")
Iterator<String> decodedLazyPost();

@RequestLine(value = "GET /api/queues/{vhost}", decodeSlash = false)
byte[] getQueues(@Param("vhost") String vhost);

Expand Down
1 change: 1 addition & 0 deletions okhttp/src/main/java/feign/okhttp/OkHttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public feign.Response execute(feign.Request input, feign.Request.Options options
requestScoped = delegate.newBuilder()
.connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
.readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
.followRedirects(options.isFollowRedirects())
.build();
} else {
requestScoped = delegate;
Expand Down
42 changes: 39 additions & 3 deletions okhttp/src/test/java/feign/okhttp/OkHttpClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

import feign.Feign.Builder;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.Response;
import feign.Request;
import feign.Util;
import feign.assertj.MockWebServerAssertions;
import feign.client.AbstractClientTest;
Expand All @@ -26,8 +26,6 @@
import okhttp3.mockwebserver.MockResponse;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;

Expand Down Expand Up @@ -57,10 +55,48 @@ public void testContentTypeWithoutCharset() throws Exception {
}


@Test
public void testNoFollowRedirect() throws Exception {
server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));

OkHttpClientTestInterface api = newBuilder()
.options(new Request.Options(1000, 1000, false))
.target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());

Response response = api.get();
// Response length should not be null
assertEquals(302, response.status());
assertEquals(server.url("redirect").toString(), response.headers().get("Location").iterator().next());

}


@Test
public void testFollowRedirect() throws Exception {
String expectedBody = "Hello";

server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));
server.enqueue(new MockResponse().setBody(expectedBody));

OkHttpClientTestInterface api = newBuilder()
.options(new Request.Options(1000, 1000, true))
.target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());

Response response = api.get();
// Response length should not be null
assertEquals(200, response.status());
assertEquals(expectedBody, response.body().toString());

}


public interface OkHttpClientTestInterface {

@RequestLine("GET /")
@Headers({"Accept: text/plain", "Content-Type: text/plain"})
Response getWithContentType();

@RequestLine("GET /")
Response get();
}
}
5 changes: 4 additions & 1 deletion ribbon/src/main/java/feign/ribbon/LBClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public final class LBClient extends
private final int readTimeout;
private final IClientConfig clientConfig;
private final Set<Integer> retryableStatusCodes;
private final Boolean followRedirects;

public static LBClient create(ILoadBalancer lb, IClientConfig clientConfig) {
return new LBClient(lb, clientConfig);
Expand All @@ -66,6 +67,7 @@ static Set<Integer> parseStatusCodes(String statusCodesString) {
connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout);
readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout);
retryableStatusCodes = parseStatusCodes(clientConfig.get(LBClientFactory.RetryableStatusCodes));
followRedirects = clientConfig.get(CommonClientConfigKey.FollowRedirects);
}

@Override
Expand All @@ -76,7 +78,8 @@ public RibbonResponse execute(RibbonRequest request, IClientConfig configOverrid
options =
new Request.Options(
configOverride.get(CommonClientConfigKey.ConnectTimeout, connectTimeout),
(configOverride.get(CommonClientConfigKey.ReadTimeout, readTimeout)));
(configOverride.get(CommonClientConfigKey.ReadTimeout, readTimeout)),
configOverride.get(CommonClientConfigKey.FollowRedirects,followRedirects));
} else {
options = new Request.Options(connectTimeout, readTimeout);
}
Expand Down
1 change: 1 addition & 0 deletions ribbon/src/main/java/feign/ribbon/RibbonClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
public FeignOptionsClientConfig(Request.Options options) {
setProperty(CommonClientConfigKey.ConnectTimeout, options.connectTimeoutMillis());
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
setProperty(CommonClientConfigKey.FollowRedirects, options.isFollowRedirects());
}

@Override
Expand Down
66 changes: 65 additions & 1 deletion ribbon/src/test/java/feign/ribbon/RibbonClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.Collection;

import org.junit.After;
import org.junit.AfterClass;
Expand All @@ -42,6 +45,7 @@
import feign.Feign;
import feign.Param;
import feign.Request;
import feign.Response;
import feign.RequestLine;
import feign.RetryableException;
import feign.Retryer;
Expand Down Expand Up @@ -277,6 +281,62 @@ public void ribbonRetryOnStatusCodes() throws IOException, InterruptedException
assertEquals(1, server1.getRequestCount());
assertEquals(1, server2.getRequestCount());
}


@Test
public void testFeignOptionsFollowRedirect() {
String expectedLocation = server2.url("").url().toString();
server1.enqueue(new MockResponse().setResponseCode(302).setHeader("Location", expectedLocation));

getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()));

Request.Options options = new Request.Options(1000, 1000, false);
TestInterface api = Feign.builder()
.options(options)
.client(RibbonClient.create())
.retryer(Retryer.NEVER_RETRY)
.target(TestInterface.class, "http://" + client());

try {
Response response = api.get();
assertEquals(302, response.status());
Collection<String> location = response.headers().get("Location");
assertNotNull(location);
assertFalse(location.isEmpty());
assertEquals(expectedLocation, location.iterator().next());
} catch (Exception ignored) {
ignored.printStackTrace();
fail("Shouldn't throw ");
}

}

@Test
public void testFeignOptionsNoFollowRedirect() {
// 302 will say go to server 2
server1.enqueue(new MockResponse().setResponseCode(302).setHeader("Location", server2.url("").url().toString()));
// server 2 will send back 200 with "Hello" as body
server2.enqueue(new MockResponse().setResponseCode(200).setBody("Hello"));

getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url()));

Request.Options options = new Request.Options(1000, 1000, true);
TestInterface api = Feign.builder()
.options(options)
.client(RibbonClient.create())
.retryer(Retryer.NEVER_RETRY)
.target(TestInterface.class, "http://" + client());

try {
Response response = api.get();
assertEquals(200, response.status());
assertEquals("Hello", response.body().toString());
} catch (Exception ignored) {
ignored.printStackTrace();
fail("Shouldn't throw ");
}

}

@Test
public void testFeignOptionsClientConfig() {
Expand All @@ -285,7 +345,8 @@ public void testFeignOptionsClientConfig() {
assertThat(config.get(CommonClientConfigKey.ConnectTimeout),
equalTo(options.connectTimeoutMillis()));
assertThat(config.get(CommonClientConfigKey.ReadTimeout), equalTo(options.readTimeoutMillis()));
assertEquals(2, config.getProperties().size());
assertThat(config.get(CommonClientConfigKey.FollowRedirects), equalTo(options.isFollowRedirects()));
assertEquals(3, config.getProperties().size());
}

@Test
Expand Down Expand Up @@ -320,5 +381,8 @@ interface TestInterface {

@RequestLine("GET /?a={a}")
void getWithQueryParameters(@Param("a") String a);

@RequestLine("GET /")
Response get();
}
}