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> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient5</artifactId>
<version>4.5.12</version> <version>5.0.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -4,7 +4,7 @@ import java.util.concurrent.TimeUnit;
public class Constants { 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_CONNECTION_TIMEOUT = 30000;
public static final int HTTP_READ_TIMEOUT = 300000; public static final int HTTP_READ_TIMEOUT = 300000;
public static final long LINK_CHECK_EXPIRATION = TimeUnit.DAYS.toMillis(1); public static final long LINK_CHECK_EXPIRATION = TimeUnit.DAYS.toMillis(1);

View file

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