From 27720739f1e2b47b174194acf0b6e5d687160103 Mon Sep 17 00:00:00 2001 From: Gcolon021 <34667267+Gcolon021@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:13:48 -0500 Subject: [PATCH] Extend HTTP client timeout to 60 seconds (#212) --- .../dbmi/avillach/util/HttpClientUtil.java | 593 +++++++++--------- 1 file changed, 291 insertions(+), 302 deletions(-) diff --git a/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java b/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java index b4f1fb2a..215d0530 100644 --- a/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java +++ b/pic-sure-util/src/main/java/edu/harvard/dbmi/avillach/util/HttpClientUtil.java @@ -45,306 +45,295 @@ import edu.harvard.dbmi.avillach.util.exception.ResourceInterfaceException; public class HttpClientUtil { - private static final ObjectMapper json = new ObjectMapper(); - - private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); - - private final HttpClient httpClient; - - public HttpClientUtil(HttpClient httpClient) { - this.httpClient = httpClient; - } - - public static boolean is2xx(HttpResponse response) { - return response.getStatusLine().getStatusCode() / 100 == 2; - } - - /** - * resource level get, which will throw a ResourceInterfaceException if cannot get response back from the url - * - * @param uri - * @param headers - * @return - */ - public HttpResponse retrieveGetResponse(String uri, Header[] headers) { - try { - logger.debug("HttpClientUtil retrieveGetResponse()"); - - //HttpClient client = getConfiguredHttpClient(); - return simpleGet(httpClient, uri, headers); - } catch (ApplicationException e) { - throw new ResourceInterfaceException(uri, e); - } - } - - public static String composeURL(String baseURL, String pathName) { - return composeURL(baseURL, pathName, null); - } - - public static String composeURL(String baseURL, String pathName, String query) { - try { - URI uri = new URI(baseURL); - List basePathComponents = Arrays.asList(uri.getPath().split("/")); - List pathNameComponents = Arrays.asList(pathName.split("/")); - List allPathComponents = new LinkedList<>(); - Predicate nonEmpty = (segment) -> { - return !segment.isEmpty(); - }; - allPathComponents.addAll(basePathComponents.stream().filter(nonEmpty).collect(Collectors.toList())); - allPathComponents.addAll(pathNameComponents.stream().filter(nonEmpty).collect(Collectors.toList())); - String queryString = query == null ? uri.getQuery() : query; - return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), - "/" + String.join("/", allPathComponents), queryString, uri.getFragment()).toString(); - } catch (URISyntaxException e) { - throw new ApplicationException("baseURL invalid : " + baseURL, e); - } - } - - /** - * resource level post, which will throw a ResourceInterfaceException if cannot get response back from the - * url - * - * @param uri - * @param headers - * @return - */ - public HttpResponse retrievePostResponse(String uri, Header[] headers, String body) { - try { - logger.debug("HttpClientUtil retrievePostResponse()"); - - List
headerList = new ArrayList<>(); - - if (headers != null) - headerList = new ArrayList<>(Arrays.asList(headers)); - headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); - - //HttpClient client = getConfiguredHttpClient(); - return simplePost(uri, httpClient, new StringEntity(body), headerList.toArray(new Header[headerList.size()])); - } catch (ApplicationException | UnsupportedEncodingException e) { - throw new ResourceInterfaceException(uri, e); - } - } - - public HttpResponse retrievePostResponse(String uri, List
headers, String body) { - return retrievePostResponse(uri, headers.toArray(new Header[headers.size()]), body); - } - - public List readListFromResponse(HttpResponse response, Class expectedElementType) { - logger.debug("HttpClientUtil readListFromResponse()"); - try { - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - return json.readValue(responseBody, new TypeReference>() { - }); - } catch (IOException e) { - throw new ApplicationException("Incorrect list type returned"); - } - } - - public String readObjectFromResponse(HttpResponse response) { - logger.debug("HttpClientUtil readObjectFromResponse(HttpResponse response)"); - try { - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - logger.debug("readObjectFromResponse() responseBody {}", responseBody); - return responseBody; - } catch (IOException e) { - throw new ApplicationException("Incorrect object type returned", e); - } - } - - public static T readObjectFromResponse(HttpResponse response, Class expectedElementType) { - logger.debug("HttpClientUtil readObjectFromResponse()"); - try { - long startTime = System.nanoTime(); - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - logger.debug( - "readObjectFromResponse() line: EntityUtils.toString(response.getEntity().getContent(), \"UTF-8\"), took {}", - (System.nanoTime() - startTime)); - logger.trace("readObjectFromResponse() responseBody {}", responseBody); - - startTime = System.nanoTime(); - T t = json.readValue(responseBody, json.getTypeFactory().constructType(expectedElementType)); - logger.debug( - "readObjectFromResponse() line: json.readValue(responseBody, json.getTypeFactory().constructType(expectedElementType)), took {}", - (System.nanoTime() - startTime)); - return t; - } catch (IOException e) { - throw new ApplicationException("Incorrect object type returned", e); - } - } - - public static void throwResponseError(HttpResponse response, String baseURL) { - String errorMessage = baseURL + " " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase(); - try { - JsonNode responseNode = json.readTree(response.getEntity().getContent()); - if (responseNode != null && responseNode.has("message")) { - errorMessage += "/n" + responseNode.get("message").asText(); - } - } catch (IOException e) { - // That's fine, there's no message - } - if (response.getStatusLine().getStatusCode() == 401) { - throw new NotAuthorizedException(errorMessage); - } - throw new ResourceInterfaceException(errorMessage); - } - - public static void throwInternalResponseError(HttpResponse response, String baseURL) { - // We don't want to propagate 401s. A site 401ing is a server side error and should - // 500 in the common area. - String errorMessage = baseURL + " " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase(); - try { - JsonNode responseNode = json.readTree(response.getEntity().getContent()); - if (responseNode != null && responseNode.has("message")) { - errorMessage += "/n" + responseNode.get("message").asText(); - } - } catch (IOException e) { - // That's fine, there's no message - } - throw new ResourceInterfaceException(errorMessage); - } - - /** - * Basic and general post function using Apache Http Client - * - * @param uri - * @param client - * @param requestBody - * @param headers - * @return HttpResponse - * @throws ApplicationException - */ - public HttpResponse simplePost(String uri, HttpClient client, StringEntity requestBody, Header... headers) - throws ApplicationException { - - HttpPost post = new HttpPost(uri); - post.setHeaders(headers); - post.setEntity(requestBody); - - try { - return httpClient.execute(post, buildHttpClientContext()); - } catch (IOException ex) { - logger.error("simplePost() Exception: {}, cannot get response by POST from url: {}", ex.getMessage(), uri); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - - /** - * Basic and general post function using Apache Http Client - * - * @param uri - * @param requestBody - * @param client - * @param headers - * @return InputStream - */ - public InputStream simplePost(String uri, StringEntity requestBody, HttpClient client, Header... headers) { - HttpResponse response = simplePost(uri, client, requestBody, headers); - - try { - return response.getEntity().getContent(); - } catch (IOException ex) { - logger.error("simplePost() cannot get content by POST from url: {} - " + ex.getLocalizedMessage(), uri); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - - /** - * for general and basic use of GET function using Apache Http Client - * - * @param client - * @param uri - * @param headers - * @return - * @throws ApplicationException - */ - public HttpResponse simpleGet(HttpClient client, String uri, Header... headers) throws ApplicationException { - HttpGet get = new HttpGet(uri); - get.setHeaders(headers); - - try { - return httpClient.execute(get, buildHttpClientContext()); - } catch (IOException ex) { - logger.error("HttpResponse simpleGet() cannot get response by GET from url: {} - " + ex.getLocalizedMessage(), uri); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - - public InputStream simpleGet(String uri, HttpClient client, Header... headers) { - return simpleGetWithConfig(uri, client, null, headers); - } - - public InputStream simpleGetWithConfig( - String uri, HttpClient client, RequestConfig config, Header... headers - ) throws ApplicationException { - HttpGet get = new HttpGet(uri); - get.setHeaders(headers); - if (config != null) { - get.setConfig(config); - } - - try { - return httpClient.execute(get, buildHttpClientContext()) - .getEntity() - .getContent(); - } catch (IOException ex) { - logger.error("InputStream simpleGet() cannot get response by GET from url: {} - " + ex.getLocalizedMessage(), uri); - throw new ApplicationException("Inner problem, please contact system admin and check the server log"); - } - } - - public static HttpClient getConfiguredHttpClient(HttpClientConnectionManager connectionManager) { - try { - int timeout = 30; - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(timeout * 1000) - .setConnectionRequestTimeout(timeout * 1000) - .setSocketTimeout(timeout * 1000).build(); - - SSLConnectionSocketFactory.getSocketFactory(); - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, null, null); - String[] defaultCiphers = sslContext.getServerSocketFactory().getDefaultCipherSuites(); - - List limited = new LinkedList(); - for(String suite : defaultCiphers) - { - //filter out Diffie-Hellman ciphers - if( ! (suite.contains("_DHE_") || suite.contains("_DH_"))) - { - limited.add(suite); - } - } - - return HttpClients.custom() - .setSSLSocketFactory(new SSLConnectionSocketFactory( - SSLContexts.createSystemDefault(), - new String[]{"TLSv1.2"}, - limited.toArray(new String[limited.size()]), - SSLConnectionSocketFactory.getDefaultHostnameVerifier())) - .setConnectionManager(connectionManager) - .setDefaultRequestConfig(config) - .build(); - } catch( NoSuchAlgorithmException | KeyManagementException e) { - logger.warn("Unable to establish SSL context. using default client", e); - } - - //default - return HttpClientBuilder.create().useSystemProperties().build(); - } - - public static HttpClientUtil getInstance(HttpClientConnectionManager connectionManager) { - return new HttpClientUtil(getConfiguredHttpClient(connectionManager)); - } - - public static void closeHttpResponse(HttpResponse resourcesResponse) { - if (resourcesResponse != null) { - try { - EntityUtils.consume(resourcesResponse.getEntity()); - } catch (IOException e) { - logger.error("Failed to close HttpResponse entity", e); - } - } - } + private static final ObjectMapper json = new ObjectMapper(); + + private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); + + private final HttpClient httpClient; + + public HttpClientUtil(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public static boolean is2xx(HttpResponse response) { + return response.getStatusLine().getStatusCode() / 100 == 2; + } + + /** + * resource level get, which will throw a ResourceInterfaceException if cannot get response back from the url + * + * @param uri + * @param headers + * @return + */ + public HttpResponse retrieveGetResponse(String uri, Header[] headers) { + try { + logger.debug("HttpClientUtil retrieveGetResponse()"); + + // HttpClient client = getConfiguredHttpClient(); + return simpleGet(httpClient, uri, headers); + } catch (ApplicationException e) { + throw new ResourceInterfaceException(uri, e); + } + } + + public static String composeURL(String baseURL, String pathName) { + return composeURL(baseURL, pathName, null); + } + + public static String composeURL(String baseURL, String pathName, String query) { + try { + URI uri = new URI(baseURL); + List basePathComponents = Arrays.asList(uri.getPath().split("/")); + List pathNameComponents = Arrays.asList(pathName.split("/")); + List allPathComponents = new LinkedList<>(); + Predicate nonEmpty = (segment) -> { + return !segment.isEmpty(); + }; + allPathComponents.addAll(basePathComponents.stream().filter(nonEmpty).collect(Collectors.toList())); + allPathComponents.addAll(pathNameComponents.stream().filter(nonEmpty).collect(Collectors.toList())); + String queryString = query == null ? uri.getQuery() : query; + return new URI( + uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), "/" + String.join("/", allPathComponents), queryString, + uri.getFragment() + ).toString(); + } catch (URISyntaxException e) { + throw new ApplicationException("baseURL invalid : " + baseURL, e); + } + } + + /** + * resource level post, which will throw a ResourceInterfaceException if cannot get response back from the url + * + * @param uri + * @param headers + * @return + */ + public HttpResponse retrievePostResponse(String uri, Header[] headers, String body) { + try { + logger.debug("HttpClientUtil retrievePostResponse()"); + + List
headerList = new ArrayList<>(); + + if (headers != null) headerList = new ArrayList<>(Arrays.asList(headers)); + headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); + + // HttpClient client = getConfiguredHttpClient(); + return simplePost(uri, httpClient, new StringEntity(body), headerList.toArray(new Header[headerList.size()])); + } catch (ApplicationException | UnsupportedEncodingException e) { + throw new ResourceInterfaceException(uri, e); + } + } + + public HttpResponse retrievePostResponse(String uri, List
headers, String body) { + return retrievePostResponse(uri, headers.toArray(new Header[headers.size()]), body); + } + + public List readListFromResponse(HttpResponse response, Class expectedElementType) { + logger.debug("HttpClientUtil readListFromResponse()"); + try { + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + return json.readValue(responseBody, new TypeReference>() {}); + } catch (IOException e) { + throw new ApplicationException("Incorrect list type returned"); + } + } + + public String readObjectFromResponse(HttpResponse response) { + logger.debug("HttpClientUtil readObjectFromResponse(HttpResponse response)"); + try { + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + logger.debug("readObjectFromResponse() responseBody {}", responseBody); + return responseBody; + } catch (IOException e) { + throw new ApplicationException("Incorrect object type returned", e); + } + } + + public static T readObjectFromResponse(HttpResponse response, Class expectedElementType) { + logger.debug("HttpClientUtil readObjectFromResponse()"); + try { + long startTime = System.nanoTime(); + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + logger.debug( + "readObjectFromResponse() line: EntityUtils.toString(response.getEntity().getContent(), \"UTF-8\"), took {}", + (System.nanoTime() - startTime) + ); + logger.trace("readObjectFromResponse() responseBody {}", responseBody); + + startTime = System.nanoTime(); + T t = json.readValue(responseBody, json.getTypeFactory().constructType(expectedElementType)); + logger.debug( + "readObjectFromResponse() line: json.readValue(responseBody, json.getTypeFactory().constructType(expectedElementType)), took {}", + (System.nanoTime() - startTime) + ); + return t; + } catch (IOException e) { + throw new ApplicationException("Incorrect object type returned", e); + } + } + + public static void throwResponseError(HttpResponse response, String baseURL) { + String errorMessage = baseURL + " " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase(); + try { + JsonNode responseNode = json.readTree(response.getEntity().getContent()); + if (responseNode != null && responseNode.has("message")) { + errorMessage += "/n" + responseNode.get("message").asText(); + } + } catch (IOException e) { + // That's fine, there's no message + } + if (response.getStatusLine().getStatusCode() == 401) { + throw new NotAuthorizedException(errorMessage); + } + throw new ResourceInterfaceException(errorMessage); + } + + public static void throwInternalResponseError(HttpResponse response, String baseURL) { + // We don't want to propagate 401s. A site 401ing is a server side error and should + // 500 in the common area. + String errorMessage = baseURL + " " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase(); + try { + JsonNode responseNode = json.readTree(response.getEntity().getContent()); + if (responseNode != null && responseNode.has("message")) { + errorMessage += "/n" + responseNode.get("message").asText(); + } + } catch (IOException e) { + // That's fine, there's no message + } + throw new ResourceInterfaceException(errorMessage); + } + + /** + * Basic and general post function using Apache Http Client + * + * @param uri + * @param client + * @param requestBody + * @param headers + * @return HttpResponse + * @throws ApplicationException + */ + public HttpResponse simplePost(String uri, HttpClient client, StringEntity requestBody, Header... headers) throws ApplicationException { + + HttpPost post = new HttpPost(uri); + post.setHeaders(headers); + post.setEntity(requestBody); + + try { + return httpClient.execute(post, buildHttpClientContext()); + } catch (IOException ex) { + logger.error("simplePost() Exception: {}, cannot get response by POST from url: {}", ex.getMessage(), uri); + throw new ApplicationException("Inner problem, please contact system admin and check the server log"); + } + } + + /** + * Basic and general post function using Apache Http Client + * + * @param uri + * @param requestBody + * @param client + * @param headers + * @return InputStream + */ + public InputStream simplePost(String uri, StringEntity requestBody, HttpClient client, Header... headers) { + HttpResponse response = simplePost(uri, client, requestBody, headers); + + try { + return response.getEntity().getContent(); + } catch (IOException ex) { + logger.error("simplePost() cannot get content by POST from url: {} - " + ex.getLocalizedMessage(), uri); + throw new ApplicationException("Inner problem, please contact system admin and check the server log"); + } + } + + /** + * for general and basic use of GET function using Apache Http Client + * + * @param client + * @param uri + * @param headers + * @return + * @throws ApplicationException + */ + public HttpResponse simpleGet(HttpClient client, String uri, Header... headers) throws ApplicationException { + HttpGet get = new HttpGet(uri); + get.setHeaders(headers); + + try { + return httpClient.execute(get, buildHttpClientContext()); + } catch (IOException ex) { + logger.error("HttpResponse simpleGet() cannot get response by GET from url: {} - " + ex.getLocalizedMessage(), uri); + throw new ApplicationException("Inner problem, please contact system admin and check the server log"); + } + } + + public InputStream simpleGet(String uri, HttpClient client, Header... headers) { + return simpleGetWithConfig(uri, client, null, headers); + } + + public InputStream simpleGetWithConfig(String uri, HttpClient client, RequestConfig config, Header... headers) + throws ApplicationException { + HttpGet get = new HttpGet(uri); + get.setHeaders(headers); + if (config != null) { + get.setConfig(config); + } + + try { + return httpClient.execute(get, buildHttpClientContext()).getEntity().getContent(); + } catch (IOException ex) { + logger.error("InputStream simpleGet() cannot get response by GET from url: {} - " + ex.getLocalizedMessage(), uri); + throw new ApplicationException("Inner problem, please contact system admin and check the server log"); + } + } + + public static HttpClient getConfiguredHttpClient(HttpClientConnectionManager connectionManager) { + try { + int timeout = 60; + RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000) + .setSocketTimeout(timeout * 1000).build(); + + SSLConnectionSocketFactory.getSocketFactory(); + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(null, null, null); + String[] defaultCiphers = sslContext.getServerSocketFactory().getDefaultCipherSuites(); + + List limited = new LinkedList(); + for (String suite : defaultCiphers) { + // filter out Diffie-Hellman ciphers + if (!(suite.contains("_DHE_") || suite.contains("_DH_"))) { + limited.add(suite); + } + } + + return HttpClients.custom() + .setSSLSocketFactory( + new SSLConnectionSocketFactory( + SSLContexts.createSystemDefault(), new String[] {"TLSv1.2"}, limited.toArray(new String[limited.size()]), + SSLConnectionSocketFactory.getDefaultHostnameVerifier() + ) + ).setConnectionManager(connectionManager).setDefaultRequestConfig(config).build(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + logger.warn("Unable to establish SSL context. using default client", e); + } + + // default + return HttpClientBuilder.create().useSystemProperties().build(); + } + + public static HttpClientUtil getInstance(HttpClientConnectionManager connectionManager) { + return new HttpClientUtil(getConfiguredHttpClient(connectionManager)); + } + + public static void closeHttpResponse(HttpResponse resourcesResponse) { + if (resourcesResponse != null) { + try { + EntityUtils.consume(resourcesResponse.getEntity()); + } catch (IOException e) { + logger.error("Failed to close HttpResponse entity", e); + } + } + } }