You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by du...@apache.org on 2020/02/13 15:00:32 UTC

[sling-org-apache-sling-testing-clients] branch master updated (609cb61 -> 3d658ce)

This is an automated email from the ASF dual-hosted git repository.

dulvac pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-clients.git.


    from 609cb61  SLING-8727 - NPE in SlingClient constructor when url doesn't have a protocol
     add c229754  SLING-9053 implementing retries
     add b288844  SLING-9053 fixing default to 5 retries
     add 5ee4461  SLING-9053 reworking the solution
     add 7869496  SLING-9053 revert to original
     add 4471382  SLING-9053 fixing code smell
     new 3d658ce  SLING-9053 Implement retries mechanism for http 5XX response codes

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/sling/testing/clients/Constants.java    |  32 +++++
 .../apache/sling/testing/clients/SlingClient.java  |  10 +-
 .../interceptors/FormBasedAuthInterceptor.java     |   9 +-
 .../apache/sling/testing/clients/package-info.java |   2 +-
 .../clients/util/ServerErrorRetryStrategy.java     |  84 +++++++++++++
 .../sling/testing/clients/util/package-info.java   |   2 +-
 .../clients/SlingClientRetryStrategyTest.java      | 132 +++++++++++++++++++++
 7 files changed, 263 insertions(+), 8 deletions(-)
 create mode 100644 src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
 create mode 100644 src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java


[sling-org-apache-sling-testing-clients] 01/01: SLING-9053 Implement retries mechanism for http 5XX response codes

Posted by du...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dulvac pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-clients.git

commit 3d658ce650da482056d61d131cc48d4890cbc94b
Author: Andrei Dulvac <ad...@adobe.com>
AuthorDate: Thu Feb 13 16:00:07 2020 +0100

    SLING-9053 Implement retries mechanism for http 5XX response codes
---
 .../apache/sling/testing/clients/Constants.java    |  24 ++--
 .../apache/sling/testing/clients/SlingClient.java  |  13 +-
 .../interceptors/FormBasedAuthInterceptor.java     |   6 +-
 .../LoggedServiceUnavailableRetryStrategy.java     |  56 ---------
 .../clients/util/ServerErrorRetryStrategy.java     |  84 +++++++++++++
 .../SlingClientRetryServiceUnavailableTest.java    |  90 --------------
 .../clients/SlingClientRetryStrategyTest.java      | 132 +++++++++++++++++++++
 7 files changed, 242 insertions(+), 163 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/clients/Constants.java b/src/main/java/org/apache/sling/testing/clients/Constants.java
index 18eafc1..43a2cf6 100644
--- a/src/main/java/org/apache/sling/testing/clients/Constants.java
+++ b/src/main/java/org/apache/sling/testing/clients/Constants.java
@@ -16,10 +16,6 @@
  */
 package org.apache.sling.testing.clients;
 
-import org.apache.http.client.ServiceUnavailableRetryStrategy;
-import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
-import org.apache.sling.testing.clients.util.LoggedServiceUnavailableRetryStrategy;
-
 public class Constants {
 
     /**
@@ -62,13 +58,21 @@ public class Constants {
     public static final long HTTP_DELAY = delay;
 
     /**
-     * Custom ServiceUnavailableRetryStrategy.
-     * Used by {@link org.apache.sling.testing.clients.SlingClient}
-     * and {@link org.apache.sling.testing.clients.interceptors.FormBasedAuthInterceptor}
+     * Number of http call retries in case of a 5XX response code
      */
-    public static final ServiceUnavailableRetryStrategy HTTP_RETRY_STRATEGY = logRetries
-            ? new LoggedServiceUnavailableRetryStrategy(retries, retriesDelay)
-            : new DefaultServiceUnavailableRetryStrategy(retries, retriesDelay);
+    public static final int HTTP_RETRIES = retries;
+
+    /**
+     * The delay in milliseconds between http retries
+     */
+    public static final int HTTP_RETRIES_DELAY = retriesDelay;
+
+    /**
+     * Whether to log or not http request retries
+     */
+    public static final boolean HTTP_LOG_RETRIES = logRetries;
+
+
 
     /**
      * Handle to OSGI console
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClient.java b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
index 4ecf731..15a20db 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
@@ -25,16 +25,16 @@ import org.apache.http.annotation.Immutable;
 import org.apache.http.client.CookieStore;
 import org.apache.http.client.CredentialsProvider;
 import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.MultipartEntityBuilder;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.sling.testing.clients.interceptors.DelayRequestInterceptor;
 import org.apache.sling.testing.clients.interceptors.TestDescriptionInterceptor;
-import org.apache.sling.testing.clients.util.FormEntityBuilder;
-import org.apache.sling.testing.clients.util.HttpUtils;
-import org.apache.sling.testing.clients.util.JsonUtils;
+import org.apache.sling.testing.clients.util.*;
 import org.apache.sling.testing.clients.util.poller.AbstractPoller;
 import org.apache.sling.testing.clients.util.poller.Polling;
 import org.codehaus.jackson.JsonNode;
@@ -626,8 +626,7 @@ public class SlingClient extends AbstractSlingClient {
         private final HttpClientBuilder httpClientBuilder;
 
         protected InternalBuilder(URI url, String user, String password) {
-            this.httpClientBuilder = HttpClientBuilder.create()
-                    .setServiceUnavailableRetryStrategy(Constants.HTTP_RETRY_STRATEGY);
+            this.httpClientBuilder = HttpClientBuilder.create();
             this.configBuilder = SlingClientConfig.Builder.create().setUrl(url).setUser(user).setPassword(password);
 
             setDefaults();
@@ -692,6 +691,10 @@ public class SlingClient extends AbstractSlingClient {
             httpClientBuilder.addInterceptorLast(new TestDescriptionInterceptor());
             httpClientBuilder.addInterceptorLast(new DelayRequestInterceptor(Constants.HTTP_DELAY));
 
+            // HTTP request strategy
+            httpClientBuilder.setServiceUnavailableRetryStrategy(new ServerErrorRetryStrategy(
+                    Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, Constants.HTTP_LOG_RETRIES));
+
             return this;
         }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java b/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
index 9360f4b..e7e251f 100644
--- a/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
+++ b/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
@@ -28,6 +28,7 @@ import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.protocol.HttpContext;
 import org.apache.sling.testing.clients.Constants;
+import org.apache.sling.testing.clients.util.ServerErrorRetryStrategy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -75,8 +76,9 @@ public class FormBasedAuthInterceptor implements HttpRequestInterceptor {
 
         HttpPost loginPost = new HttpPost(URI.create(request.getRequestLine().getUri()).resolve(loginPath));
         loginPost.setEntity(httpEntity);
-        final CloseableHttpClient client = HttpClientBuilder.create()
-                .setServiceUnavailableRetryStrategy(Constants.HTTP_RETRY_STRATEGY)
+        final CloseableHttpClient client = HttpClientBuilder.create().setServiceUnavailableRetryStrategy(
+                new ServerErrorRetryStrategy(
+                        Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, Constants.HTTP_LOG_RETRIES))
                 .disableRedirectHandling().build();
 
         client.execute(host, loginPost, context);
diff --git a/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java b/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java
deleted file mode 100644
index 1303b7b..0000000
--- a/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You 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
- *
- * http://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.apache.sling.testing.clients.util;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.annotation.Immutable;
-import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-@Immutable
-public class LoggedServiceUnavailableRetryStrategy extends DefaultServiceUnavailableRetryStrategy {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(LoggedServiceUnavailableRetryStrategy.class);
-
-    public LoggedServiceUnavailableRetryStrategy(final int maxRetries, final int retryInterval) {
-        super(maxRetries, retryInterval);
-    }
-
-    @Override
-    public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
-        boolean needRetry = super.retryRequest(response, executionCount, context);
-        if (needRetry && LOGGER.isWarnEnabled()) {
-            LOGGER.warn("Request retry needed due to service unavailable response");
-            LOGGER.warn("Response headers contained:");
-            Arrays.stream(response.getAllHeaders()).forEach(h -> LOGGER.warn("Header {}:{}", h.getName(), h.getValue()));
-            try {
-                String content = EntityUtils.toString(response.getEntity());
-                LOGGER.warn("Response content: {}", content);
-            } catch (IOException exc) {
-                LOGGER.warn("Response as no content");
-            }
-        }
-        return needRetry;
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java b/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
new file mode 100644
index 0000000..bcce9e4
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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
+ *
+ * http://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.apache.sling.testing.clients.util;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * {code ServiceUnavailableRetryStrategy} strategy for retrying request in case of a 5XX response code
+ */
+public class ServerErrorRetryStrategy implements ServiceUnavailableRetryStrategy {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServerErrorRetryStrategy.class);
+
+    private final int maxRetries;
+    private final int retryInterval;
+    private final boolean logRetries;
+
+    public ServerErrorRetryStrategy(final int maxRetries, final int retryInterval, final boolean logRetries) {
+        super();
+        Args.positive(maxRetries, "Max retries");
+        Args.positive(retryInterval, "Retry interval");
+        this.maxRetries = maxRetries;
+        this.retryInterval = retryInterval;
+        this.logRetries = logRetries;
+    }
+
+    public ServerErrorRetryStrategy(int maxRetries, int retryInterval) {
+        this (1, 1000, true);
+    }
+
+    public ServerErrorRetryStrategy() {
+        this(1, 1000);
+    }
+
+    @Override
+    public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
+        boolean needsRetry = executionCount <= this.maxRetries &&
+                response.getStatusLine().getStatusCode() >= HttpStatus.SC_INTERNAL_SERVER_ERROR &&
+                response.getStatusLine().getStatusCode() < HttpStatus.SC_INTERNAL_SERVER_ERROR + 100;
+
+        if (this.logRetries && needsRetry && LOG.isWarnEnabled()) {
+            LOG.warn("Request retry needed due to service unavailable response");
+            LOG.warn("Response headers contained:");
+            Arrays.stream(response.getAllHeaders()).forEach(h -> LOG.warn("Header {}:{}", h.getName(), h.getValue()));
+            try {
+                String content = EntityUtils.toString(response.getEntity());
+                LOG.warn("Response content: {}", content);
+            } catch (IOException exc) {
+                LOG.warn("Response as no content");
+            }
+        }
+        return needsRetry;
+    }
+
+    @Override
+    public long getRetryInterval() {
+        return retryInterval;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java
deleted file mode 100644
index 2670983..0000000
--- a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You 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
- *
- * http://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.apache.sling.testing.clients;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class SlingClientRetryServiceUnavailableTest {
-    private static final String GET_UNAVAILABLE_PATH = "/test/unavailable/resource";
-    private static final String NOK_RESPONSE = "TEST_NOK";
-    private static final String OK_RESPONSE = "TEST_OK";
-
-    private static final int MAX_RETRIES = 5;
-
-    private static int requestCount = 0;
-    private static int availableAtRequestCount = Integer.MAX_VALUE;
-
-    static {
-        System.setProperty(Constants.CONFIG_PROP_PREFIX + "http.logRetries", "true");
-    }
-
-    @ClassRule
-    public static HttpServerRule httpServer = new HttpServerRule() {
-        @Override
-        protected void registerHandlers() throws IOException {
-            serverBootstrap.registerHandler(GET_UNAVAILABLE_PATH, new HttpRequestHandler() {
-                @Override
-                public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
-                    requestCount++;
-                    if (requestCount == availableAtRequestCount) {
-                        response.setEntity(new StringEntity(OK_RESPONSE));
-                        response.setStatusCode(200);
-                    } else {
-                        response.setEntity(new StringEntity(NOK_RESPONSE));
-                        response.setStatusCode(503);
-                    }
-                }
-            });
-        }
-    };
-
-    @Test
-    public void testRetryReallyUnavailable() throws Exception {
-        requestCount = 0;
-        availableAtRequestCount = Integer.MAX_VALUE; // never available
-        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
-        SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 503, 10);
-        assertEquals(MAX_RETRIES + 1, requestCount);
-        assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
-    }
-
-    @Test
-    public void testRetryEventuallyAvailable() throws Exception {
-        requestCount = 0;
-        availableAtRequestCount = 3;
-        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
-        SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 200, 10);
-        assertEquals(availableAtRequestCount, requestCount);
-        assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
-
-    }
-
-}
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
new file mode 100644
index 0000000..23f3b63
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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
+ *
+ * http://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.apache.sling.testing.clients;
+
+import org.apache.http.entity.StringEntity;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+public class SlingClientRetryStrategyTest {
+    private static final String GET_UNAVAILABLE_PATH = "/test/unavailable/resource";
+    private static final String GET_INEXISTENT_PATH = "/test/inexistent/resource";
+    private static final String GET_INTERNAL_ERROR_PATH = "/test/internalerror/resource";
+    private static final String NOK_RESPONSE = "TEST_NOK";
+    private static final String OK_RESPONSE = "TEST_OK";
+
+    private static final int MAX_RETRIES = 5;
+
+    private static int requestCount = 0;
+    private static int availableAtRequestCount = Integer.MAX_VALUE;
+
+    static {
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + Constants.HTTP_LOG_RETRIES, "true");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + Constants.HTTP_DELAY, "50");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + Constants.HTTP_RETRIES, "5");
+    }
+
+    @ClassRule
+    public static HttpServerRule httpServer = new HttpServerRule() {
+        @Override
+        protected void registerHandlers() throws IOException {
+            serverBootstrap.registerHandler(GET_UNAVAILABLE_PATH, (request, response, context) -> {
+                requestCount++;
+                if (requestCount == availableAtRequestCount) {
+                    response.setEntity(new StringEntity(OK_RESPONSE));
+                    response.setStatusCode(200);
+                } else {
+                    response.setEntity(new StringEntity(NOK_RESPONSE));
+                    response.setStatusCode(503);
+                }
+            });
+
+            serverBootstrap.registerHandler(GET_INTERNAL_ERROR_PATH, (request, response, context) -> {
+                requestCount++;
+                if (requestCount == availableAtRequestCount) {
+                    response.setEntity(new StringEntity(OK_RESPONSE));
+                    response.setStatusCode(200);
+                } else {
+                    response.setEntity(new StringEntity(NOK_RESPONSE));
+                    response.setStatusCode(500);
+                }
+            });
+
+            serverBootstrap.registerHandler(GET_INEXISTENT_PATH, (request, response, context) -> {
+                requestCount++;
+                response.setEntity(new StringEntity(NOK_RESPONSE));
+                response.setStatusCode(404);
+            });
+        }
+    };
+
+    @Test
+    public void testRetryReallyUnavailable() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = Integer.MAX_VALUE; // never available
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 503);
+        assertEquals(MAX_RETRIES + 1, requestCount);
+        assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+    }
+
+    @Test
+    public void testRetryReallyInternalError() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = Integer.MAX_VALUE; // never available
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_INTERNAL_ERROR_PATH, 500);
+        assertEquals(MAX_RETRIES + 1, requestCount);
+        assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+    }
+
+    @Test
+    public void testRetryInexistent() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = Integer.MAX_VALUE; // never available
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_INEXISTENT_PATH, 404);
+        // should not retry at all
+        assertEquals(1, requestCount);
+        assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+    }
+
+    @Test
+    public void testRetryEventuallyAvailable() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = 3;
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 200);
+        assertEquals(availableAtRequestCount, requestCount);
+        assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
+
+    }
+
+    @Test
+    public void testRetryEventuallyNoError() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = 3;
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_INTERNAL_ERROR_PATH, 200);
+        assertEquals(availableAtRequestCount, requestCount);
+        assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
+
+    }
+
+}