org.testcontainers.containers.wait.HttpWaitStrategy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testcontainers Show documentation
Show all versions of testcontainers Show documentation
Isolated container management for Java code testing
package org.testcontainers.containers.wait;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import org.rnorth.ducttape.TimeoutException;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.ContainerLaunchException;
import org.testcontainers.containers.GenericContainer;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess;
/**
* Waits until an HTTP(S) endpoint returns a given status code.
*
* @author Pete Cornish {@literal }
*/
public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy {
/**
* Authorization HTTP header.
*/
private static final String HEADER_AUTHORIZATION = "Authorization";
/**
* Basic Authorization scheme prefix.
*/
private static final String AUTH_BASIC = "Basic ";
private String path = "/";
private int statusCode = HttpURLConnection.HTTP_OK;
private boolean tlsEnabled;
private String username;
private String password;
/**
* Waits for the given status code.
*
* @param statusCode the expected status code
* @return this
*/
public HttpWaitStrategy forStatusCode(int statusCode) {
this.statusCode = statusCode;
return this;
}
/**
* Waits for the given path.
*
* @param path the path to check
* @return this
*/
public HttpWaitStrategy forPath(String path) {
this.path = path;
return this;
}
/**
* Indicates that the status check should use HTTPS.
*
* @return this
*/
public HttpWaitStrategy usingTls() {
this.tlsEnabled = true;
return this;
}
/**
* Authenticate with HTTP Basic Authorization credentials.
*
* @param username the username
* @param password the password
* @return this
*/
public HttpWaitStrategy withBasicCredentials(String username, String password) {
this.username = username;
this.password = password;
return this;
}
@Override
protected void waitUntilReady() {
final Integer livenessCheckPort = getLivenessCheckPort();
if (null == livenessCheckPort) {
logger().warn("No exposed ports or mapped ports - cannot wait for status");
return;
}
final String uri = buildLivenessUri(livenessCheckPort).toString();
logger().info("Waiting for {} seconds for URL: {}", startupTimeout.getSeconds(), uri);
// try to connect to the URL
try {
retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> {
getRateLimiter().doWhenReady(() -> {
try {
final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
// authenticate
if (!Strings.isNullOrEmpty(username)) {
connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password));
connection.setUseCaches(false);
}
connection.setRequestMethod("GET");
connection.connect();
if (statusCode != connection.getResponseCode()) {
throw new RuntimeException(String.format("HTTP response code was: %s",
connection.getResponseCode()));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
return true;
});
} catch (TimeoutException e) {
throw new ContainerLaunchException(String.format(
"Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode));
}
}
/**
* Build the URI on which to check if the container is ready.
*
* @param livenessCheckPort the liveness port
* @return the liveness URI
*/
private URI buildLivenessUri(int livenessCheckPort) {
final String scheme = (tlsEnabled ? "https" : "http") + "://";
final String host = container.getContainerIpAddress();
final String portSuffix;
if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) {
portSuffix = "";
} else {
portSuffix = ":" + String.valueOf(livenessCheckPort);
}
return URI.create(scheme + host + portSuffix + path);
}
/**
* @param username the username
* @param password the password
* @return a basic authentication string for the given credentials
*/
private String buildAuthString(String username, String password) {
return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes());
}
}