KEYCLOAK-14862 Upgrade httpclient test dependency

The new version of httpclient supports handling HTTP 429 errors. This should
resolve the sporadic failures in the ReleaseNotesTest in PRs.

I also increased the retry count to 5, to make sure the default backoff
algorithm in httpclient has room to work effectively.
This commit is contained in:
Alex Szczuczko 2020-07-22 07:37:53 -06:00 committed by Bruno Oliveira da Silva
parent aecad05a10
commit 8a3fbabf26
3 changed files with 126 additions and 79 deletions

View file

@ -119,9 +119,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>

View file

@ -4,7 +4,7 @@ import java.util.concurrent.TimeUnit;
public class Constants {
public static final int HTTP_RETRY = 3;
public static final int HTTP_RETRY = 5;
public static final int HTTP_CONNECTION_TIMEOUT = 30000;
public static final int HTTP_READ_TIMEOUT = 300000;
public static final long LINK_CHECK_EXPIRATION = TimeUnit.DAYS.toMillis(1);

View file

@ -1,106 +1,153 @@
package org.keycloak.documentation.test.utils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpHead;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.TimeValue;
import java.io.IOException;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
public class HttpUtils {
public Response load(String url) {
CloseableHttpClient client = createClient();
Response response = new Response();
private CloseableHttpClient client;
public HttpUtils() {
try {
HttpGet h = new HttpGet(url);
CloseableHttpResponse r = client.execute(h);
int status = r.getStatusLine().getStatusCode();
if (status == 200) {
response.setSuccess(true);
HttpEntity entity = r.getEntity();
String c = IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);
response.setContent(c);
} else if (status == 301 || status == 302) {
String location = r.getFirstHeader("Location").getValue();
response.setRedirectLocation(location);
response.setSuccess(false);
} else {
response.setError("invalid status code " + status);
response.setSuccess(false);
}
client = createClient();
} catch (Exception e) {
response.setError("exception " + e.getMessage());
response.setSuccess(false);
} finally {
try {
client.close();
} catch (IOException e) {
}
throw new RuntimeException(e);
}
return response;
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
client.close();
} catch (IOException e) {
}
}
});
}
private CloseableHttpClient createClient() {
return HttpClientBuilder.create()
.setRetryHandler(new DefaultHttpRequestRetryHandler(Constants.HTTP_RETRY, true))
.setDefaultRequestConfig(
RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build()
)
.build();
public Response load(String url) {
return exec(new HttpGet(url));
}
public Response isValid(String url) {
CloseableHttpClient client = createClient();
return exec(new HttpHead(url));
}
private Response exec(HttpUriRequestBase method) {
Response response = new Response();
try {
HttpHead h = new HttpHead(url);
CloseableHttpResponse r = client.execute(h);
int status = r.getStatusLine().getStatusCode();
HttpClientResponseHandler<String> responseHandler = new HttpClientResponseHandler<String>() {
@Override
public String handleResponse(ClassicHttpResponse r) throws IOException {
int status = r.getCode();
if (status == 200) {
response.setSuccess(true);
} else if (status == 301 || status == 302) {
String location = r.getFirstHeader("Location").getValue();
response.setRedirectLocation(location);
response.setSuccess(false);
} else {
response.setError("invalid status code " + status);
response.setSuccess(false);
if (status == HttpStatus.SC_SUCCESS) {
response.setSuccess(true);
HttpEntity entity = r.getEntity();
try {
String c = entity != null ? EntityUtils.toString(entity) : "";
response.setContent(c);
} catch (ParseException e) {
throw new ClientProtocolException(e);
}
} else if (status / 100 == 3) {
String location = r.getFirstHeader("Location").getValue();
response.setRedirectLocation(location);
response.setSuccess(false);
} else {
response.setError("invalid status code " + status);
response.setSuccess(false);
}
return "";
}
};
try {
client.execute(method, responseHandler);
} catch (Exception e) {
e.printStackTrace();
response.setError("exception " + e.getMessage());
response.setSuccess(false);
} finally {
try {
client.close();
} catch (IOException e) {
}
}
return response;
}
private static CloseableHttpClient createClient() throws Exception {
return HttpClientBuilder.create()
.setRetryStrategy(new DefaultHttpRequestRetryStrategy(
Constants.HTTP_RETRY,
TimeValue.ofSeconds(1L)
))
.disableCookieManagement()
.disableRedirectHandling()
.setConnectionManager(
PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(new NoopSSLConnectionSocketFactory())
.build()
)
.build();
}
private static class NoopSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
private static SSLContext sslContext;
static {
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(
null,
new X509TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
},
null
);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public NoopSSLConnectionSocketFactory() {
super(sslContext, new NoopHostnameVerifier());
}
@Override
protected void verifySession(String hostname, SSLSession sslSession) throws SSLException {
// no-op
}
}
public static class Response {
private boolean success;