Skip to content

Commit

Permalink
added read timeout to http wait strategy (#2058)
Browse files Browse the repository at this point in the history
This commit adds the possibility to add a read timeout to the HTTP call
for the HTTP wait strategy.
This is necessary, because with the default Docker settings for Mac
can lead to a blocking call to `HttpURLConnection.connect()`. The follow
up issue is, that the container will never get healthy and due to
automated integration tests the tests are not able to run.
The main cause of the problem is the lazy connection establishment
during setting up the container. So the time between starting the
container and starting the wait strategy checks.

The read timeout lets the HTTP call fail, so the `retryUntilSuccess()`
is able to send another HTTP call in order to reestablish the connection
and not block the whole thread.
  • Loading branch information
Dominik Weidenfeld authored and bsideup committed Nov 11, 2019
1 parent 7e5adad commit 3142e58
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.rnorth.ducttape.TimeoutException;
import org.testcontainers.containers.ContainerLaunchException;
Expand All @@ -13,7 +12,9 @@
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.time.Duration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy {
private Predicate<String> responsePredicate;
private Predicate<Integer> statusCodePredicate = null;
private Optional<Integer> livenessPort = Optional.empty();
private Duration readTimeout = Duration.ofSeconds(1);

/**
* Waits for the given status code.
Expand Down Expand Up @@ -108,6 +110,20 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) {
return this;
}

/**
* Set the HTTP connections read timeout.
*
* @param timeout the timeout (minimum 1 millisecond)
* @return this
*/
public HttpWaitStrategy withReadTimeout(Duration timeout) {
if (timeout.toMillis() < 1) {
throw new IllegalArgumentException("you cannot specify a value smaller than 1 ms");
}
this.readTimeout = timeout;
return this;
}

/**
* Waits for the response to pass the given predicate
* @param responsePredicate The predicate to test the response against
Expand Down Expand Up @@ -143,6 +159,7 @@ protected void waitUntilReady() {
getRateLimiter().doWhenReady(() -> {
try {
final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis()));

// authenticate
if (!Strings.isNullOrEmpty(username)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.rnorth.ducttape.RetryCountExceededException;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;

Expand Down Expand Up @@ -131,6 +132,15 @@ public void testWaitUntilReadyWithSpecificPort() {
));
}

@Test
public void testWaitUntilReadyWithTimoutCausedByReadTimeout() {
waitUntilReadyAndTimeout(
startContainerWithCommand(createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090),
createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)),
9090
));
}

/**
* @param ready the AtomicBoolean on which to indicate success
* @return the WaitStrategy under test
Expand All @@ -143,6 +153,7 @@ protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) {

/**
* Create a HttpWaitStrategy instance with a waitUntilReady implementation
*
* @param ready Indicates that the WaitStrategy has completed waiting successfully.
* @return the HttpWaitStrategy instance
*/
Expand All @@ -163,9 +174,9 @@ private String createShellCommand(String header, String responseBody) {

private String createShellCommand(String header, String responseBody, int port) {
int length = responseBody.getBytes().length;
return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+
"Content-Type: text/html"+NEWLINE+
"Content-Length: "+length +NEWLINE+ "\";"
+" echo \""+responseBody+"\";} | nc -lp " + port + "; done";
return "while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE +
"Content-Type: text/html" + NEWLINE +
"Content-Length: " + length + NEWLINE + "\";"
+ " echo \"" + responseBody + "\";} | nc -lp " + port + "; done";
}
}

0 comments on commit 3142e58

Please sign in to comment.