diff --git a/client/src/main/java/se/fortnox/reactivewizard/client/HttpClient.java b/client/src/main/java/se/fortnox/reactivewizard/client/HttpClient.java index 1b8414d3..acb51a03 100644 --- a/client/src/main/java/se/fortnox/reactivewizard/client/HttpClient.java +++ b/client/src/main/java/se/fortnox/reactivewizard/client/HttpClient.java @@ -79,6 +79,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static reactor.core.Exceptions.isRetryExhausted; import static reactor.core.publisher.Mono.just; +import static se.fortnox.reactivewizard.client.HttpClientConfig.DEFAULT_TIMEOUT_MS; public class HttpClient implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class); @@ -103,8 +104,8 @@ public class HttpClient implements InvocationHandler { private final RequestLogger requestLogger; private final Map, List> beanParamCache = new ConcurrentHashMap<>(); private final Map jaxRsMetaMap = new ConcurrentHashMap<>(); - private int timeout = 10; - private TemporalUnit timeoutUnit = ChronoUnit.SECONDS; + private int timeout = DEFAULT_TIMEOUT_MS; + private TemporalUnit timeoutUnit = ChronoUnit.MILLIS; private final Duration retryDuration; @Inject @@ -131,6 +132,7 @@ public HttpClient(HttpClientConfig config, collector = new ByteBufCollector(config.getMaxResponseSize()); this.preRequestHooks = preRequestHooks; this.retryDuration = Duration.ofMillis(config.getRetryDelayMs()); + setTimeout(config.getTimeoutMs(), ChronoUnit.MILLIS); } public HttpClient(HttpClientConfig config) { @@ -534,7 +536,22 @@ protected RequestBuilder createRequest(Method method, Object[] arguments) { JaxRsMeta meta = getJaxRsMeta(method); RequestBuilder request = new RequestBuilder(serverInfo, meta.getHttpMethod(), meta.getFullPath()); - request.setUri(getPath(method, arguments, meta)); + + String root = config.getRoot(); + String path = getPath(method, arguments, meta); + if (root == null || root.isEmpty()) { + request.setUri(path); + } else { + if (root.endsWith("/")) { + root = root.substring(0, root.length() - 1); + } + if (path.startsWith("/")) { + request.setUri(root + path); + } else { + request.setUri(root + "/" + path); + } + } + setHeaderParams(request, method, arguments); addCustomParams(request, method, arguments); diff --git a/client/src/main/java/se/fortnox/reactivewizard/client/HttpClientConfig.java b/client/src/main/java/se/fortnox/reactivewizard/client/HttpClientConfig.java index f31355b9..034831fe 100644 --- a/client/src/main/java/se/fortnox/reactivewizard/client/HttpClientConfig.java +++ b/client/src/main/java/se/fortnox/reactivewizard/client/HttpClientConfig.java @@ -19,6 +19,8 @@ @Config("httpClient") public class HttpClientConfig { + public static final int DEFAULT_TIMEOUT_MS = 10_000; + @Valid @JsonProperty("port") private int port = 80; @@ -33,6 +35,7 @@ public class HttpClientConfig { private int maxConnections = 1000; private String url; + private String root; private InetSocketAddress devServerInfo; @Size(min = 1) private String devCookie; @@ -44,10 +47,12 @@ public class HttpClientConfig { private boolean isHttps; private int retryCount = 3; private int retryDelayMs = 1000; + private int timeoutMs = DEFAULT_TIMEOUT_MS; private int readTimeoutMs = 10000; private int poolAcquireTimeoutMs = 10000; @JsonProperty("validateCertificates") private boolean isValidateCertificates = true; + private boolean followRedirect = false; private long connectionMaxIdleTimeInMs = TimeUnit.MILLISECONDS.convert(10, MINUTES); private int numberOfConnectionFailuresAllowed = 10; @@ -88,6 +93,7 @@ public void setUrl(String url) throws URISyntaxException { } URI uri = new URI(this.url); setHost(uri.getHost()); + setRoot(uri.getPath()); isHttps = "https".equals(uri.getScheme()); port = uri.getPort(); @@ -100,6 +106,14 @@ public void setUrl(String url) throws URISyntaxException { } } + public void setRoot(String root) { + this.root = root; + } + + public String getRoot() { + return root; + } + public InetSocketAddress getDevServerInfo() { return devServerInfo; } @@ -161,6 +175,14 @@ public void setHost(String host) { } } + public int getTimeoutMs() { + return timeoutMs; + } + + public void setTimeoutMs(int timeoutMs) { + this.timeoutMs = timeoutMs; + } + public int getReadTimeoutMs() { return readTimeoutMs; } @@ -177,6 +199,14 @@ public void setValidateCertificates(boolean value) { isValidateCertificates = value; } + public boolean isFollowRedirect() { + return followRedirect; + } + + public void setFollowRedirect(boolean value) { + followRedirect = value; + } + public BasicAuthConfig getBasicAuth() { return basicAuth; } diff --git a/client/src/main/java/se/fortnox/reactivewizard/client/ReactorRxClientProvider.java b/client/src/main/java/se/fortnox/reactivewizard/client/ReactorRxClientProvider.java index ca5b5ea6..2755f1b3 100644 --- a/client/src/main/java/se/fortnox/reactivewizard/client/ReactorRxClientProvider.java +++ b/client/src/main/java/se/fortnox/reactivewizard/client/ReactorRxClientProvider.java @@ -75,7 +75,7 @@ private HttpClient buildClient(InetSocketAddress socketAddress) { .doOnError((httpClientRequest, throwable) -> { healthRecorder.logStatus(connectionProvider, errorCount.incrementAndGet() <= config.getNumberOfConnectionFailuresAllowed()); }, (httpClientResponse, throwable) -> { }) - .followRedirect(false); + .followRedirect(config.isFollowRedirect()); if (config.isHttps()) { return setupSsl(client, config.isValidateCertificates()); diff --git a/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientConfigTest.java b/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientConfigTest.java index 25fae64b..db14640b 100644 --- a/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientConfigTest.java +++ b/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientConfigTest.java @@ -32,11 +32,14 @@ void shouldInitializeFromConfig() { assertThat(config.getRetryCount()).isEqualTo(15); assertThat(config.getRetryDelayMs()).isEqualTo(200); assertThat(config.getMaxResponseSize()).isEqualTo(512); + assertThat(config.getTimeoutMs()).isEqualTo(60 * 1000); + assertThat(config.isFollowRedirect()).isTrue(); assertThat(config.getDevCookie()).isEqualTo("TEST=123"); assertThat(config.getDevServerInfo().getHostString()).isEqualTo("mymachine"); assertThat(config.getDevServerInfo().getPort()).isEqualTo(9090); assertThat(config.getDevHeaders()).contains(entry("Host", "localhost")); + } @Test @@ -83,6 +86,14 @@ void shouldSetPort() throws URISyntaxException { assertThat(config.getHost()).isEqualTo("localhost"); } + @Test + void shouldSetRoot() throws URISyntaxException { + assertThat(new HttpClientConfig("http://localhost:8080").getRoot()).isEqualTo(""); + assertThat(new HttpClientConfig("http://localhost:8080/").getRoot()).isEqualTo("/"); + assertThat(new HttpClientConfig("http://localhost:8080/root/path").getRoot()).isEqualTo("/root/path"); + assertThat(new HttpClientConfig("http://localhost:8080/root/path/").getRoot()).isEqualTo("/root/path/"); + } + @Test void shouldSetHttpsFromProtocolEvenIfPortIsSupplied() throws URISyntaxException { HttpClientConfig config = new HttpClientConfig("https://localhost:8080"); @@ -121,4 +132,16 @@ void shouldNotBeInsecureByDefault() throws URISyntaxException { assertThat(httpClientConfig.isHttps()).isTrue(); assertThat(httpClientConfig.isValidateCertificates()).isTrue(); } + + @Test + void shouldNotFollowRedirectByDefault() throws URISyntaxException { + HttpClientConfig httpClientConfig = new HttpClientConfig("http://example.com"); + assertThat(httpClientConfig.isFollowRedirect()).isFalse(); + } + + @Test + void shouldDefaultToTenSecondTimeout() throws URISyntaxException { + HttpClientConfig httpClientConfig = new HttpClientConfig("http://example.com"); + assertThat(httpClientConfig.getTimeoutMs()).isEqualTo(10 * 1000); + } } diff --git a/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientTest.java b/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientTest.java index 6735ca50..8edf97cd 100644 --- a/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientTest.java +++ b/client/src/test/java/se/fortnox/reactivewizard/client/HttpClientTest.java @@ -308,6 +308,23 @@ protected JaxRsMeta getJaxRsMeta(Method method) { assertThat(jaxRsMetas.get(2)).isSameAs(jaxRsMetas.get(3)); } + @Test + void shouldAddRootToRequestUri() throws URISyntaxException, NoSuchMethodException { + + assertAddedRoot("localhost", "/hello"); + assertAddedRoot("localhost/", "/hello"); + assertAddedRoot("localhost/root", "/root/hello"); + assertAddedRoot("localhost/root/", "/root/hello"); + } + + private void assertAddedRoot(String url, String expected) throws URISyntaxException, NoSuchMethodException { + HttpClient httpClient = new HttpClient(new HttpClientConfig(url)); + + Method getHello = TestResource.class.getMethod("getHello"); + + assertThat(httpClient.createRequest(getHello, new Object[0]).getUri()).isEqualTo(expected); + } + @Test void shouldRetryIfEmptyReturnedOnGet() { diff --git a/client/src/test/resources/httpconfig.yml b/client/src/test/resources/httpconfig.yml index 817897a1..1cd1438d 100644 --- a/client/src/test/resources/httpconfig.yml +++ b/client/src/test/resources/httpconfig.yml @@ -9,3 +9,5 @@ httpClient: retryCount: 15 retryDelayMs: 200 maxResponseSize: 512 + timeoutMs: 60000 + followRedirect: true