Merge pull request #10639 from 4Science/task/main/DURACOM-109

Proxy Configuration and Connection Leak using HTTP Clients
This commit is contained in:
Tim Donohue
2025-04-29 14:11:05 -05:00
committed by GitHub
34 changed files with 869 additions and 446 deletions

View File

@@ -7,4 +7,5 @@
<!-- TODO: We should have these turned on. But, currently there's a known bug with indentation checks
on JMockIt Expectations blocks and similar. See https://github.com/checkstyle/checkstyle/issues/3739 -->
<suppress checks="Indentation" files="src[/\\]test[/\\]java"/>
<suppress checks="Regexp" files="DSpaceHttpClientFactory\.java"/>
</suppressions>

View File

@@ -136,5 +136,22 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
<module name="OneStatementPerLine"/>
<!-- Require that "catch" statements are not empty (must at least contain a comment) -->
<module name="EmptyCatchBlock"/>
<!-- Require to use DSpaceHttpClientFactory.getClient() statement instead of creating directly the client -->
<module name="Regexp">
<property name="format" value="HttpClientBuilder\.create\s*\(\s*\)" />
<property name="message" value="Use DSpaceHttpClientFactory.getClient() instead of HttpClientBuilder.create()" />
<property name="illegalPattern" value="true"/>
<property name="ignoreComments" value="true"/>
</module>
<!-- Require to use DSpaceHttpClientFactory.getClient() statement instead of creating directly the client -->
<module name="Regexp">
<property name="format" value="HttpClients\.createDefault\s*\(\s*\)" />
<property name="message" value="Use DSpaceHttpClientFactory.getClient() instead of HttpClients.createDefault()" />
<property name="illegalPattern" value="true"/>
<property name="ignoreComments" value="true"/>
</module>
</module>
</module>

View File

@@ -774,6 +774,7 @@
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<!-- Used for Solr core export/import -->
<dependency>
<groupId>com.opencsv</groupId>
@@ -873,7 +874,7 @@
<artifactId>jclouds-core</artifactId>
<version>${jclouds.version}</version>
<exclusions>
<exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
@@ -942,5 +943,10 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,152 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.client;
import static org.apache.commons.collections4.ListUtils.emptyIfNull;
import java.util.List;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.dspace.services.ConfigurationService;
import org.dspace.utils.DSpace;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Factory of {@link HttpClient} with common configurations.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class DSpaceHttpClientFactory {
@Autowired
private ConfigurationService configurationService;
@Autowired
private DSpaceProxyRoutePlanner proxyRoutePlanner;
@Autowired(required = false)
private List<HttpRequestInterceptor> requestInterceptors;
@Autowired(required = false)
private List<HttpResponseInterceptor> responseInterceptors;
/**
* Get an instance of {@link DSpaceHttpClientFactory} from the Spring context.
* @return the bean instance
*/
public static DSpaceHttpClientFactory getInstance() {
return new DSpace().getSingletonService(DSpaceHttpClientFactory.class);
}
/**
* Build an instance of {@link HttpClient} setting the proxy if configured.
*
* @return the client
*/
public CloseableHttpClient build() {
return build(HttpClientBuilder.create(), true);
}
/**
* return a Builder if an instance of {@link HttpClient} pre-setting the proxy if configured.
*
* @return the client
*/
public HttpClientBuilder builder(boolean setProxy) {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
if (setProxy) {
clientBuilder.setRoutePlanner(proxyRoutePlanner);
}
getRequestInterceptors().forEach(clientBuilder::addInterceptorLast);
getResponseInterceptors().forEach(clientBuilder::addInterceptorLast);
return clientBuilder;
}
/**
* Build an instance of {@link HttpClient} without setting the proxy, even if
* configured.
*
* @return the client
*/
public CloseableHttpClient buildWithoutProxy() {
return build(HttpClientBuilder.create(), false);
}
/**
* Build an instance of {@link HttpClient} setting the proxy if configured,
* disabling automatic retries and setting the maximum total connection.
*
* @param maxConnTotal the maximum total connection value
* @return the client
*/
public CloseableHttpClient buildWithoutAutomaticRetries(int maxConnTotal) {
HttpClientBuilder clientBuilder = HttpClientBuilder.create()
.disableAutomaticRetries()
.setMaxConnTotal(maxConnTotal);
return build(clientBuilder, true);
}
/**
* Build an instance of {@link HttpClient} setting the proxy if configured with
* the given request configuration.
* @param requestConfig the request configuration
* @return the client
*/
public CloseableHttpClient buildWithRequestConfig(RequestConfig requestConfig) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig);
return build(httpClientBuilder, true);
}
private CloseableHttpClient build(HttpClientBuilder clientBuilder, boolean setProxy) {
if (setProxy) {
clientBuilder.setRoutePlanner(proxyRoutePlanner);
}
getRequestInterceptors().forEach(clientBuilder::addInterceptorLast);
getResponseInterceptors().forEach(clientBuilder::addInterceptorLast);
return clientBuilder.build();
}
public ConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
public List<HttpRequestInterceptor> getRequestInterceptors() {
return emptyIfNull(requestInterceptors);
}
public void setRequestInterceptors(List<HttpRequestInterceptor> requestInterceptors) {
this.requestInterceptors = requestInterceptors;
}
public List<HttpResponseInterceptor> getResponseInterceptors() {
return emptyIfNull(responseInterceptors);
}
public void setResponseInterceptors(List<HttpResponseInterceptor> responseInterceptors) {
this.responseInterceptors = responseInterceptors;
}
public DSpaceProxyRoutePlanner getProxyRoutePlanner() {
return proxyRoutePlanner;
}
public void setProxyRoutePlanner(DSpaceProxyRoutePlanner proxyRoutePlanner) {
this.proxyRoutePlanner = proxyRoutePlanner;
}
}

View File

@@ -0,0 +1,73 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.client;
import java.util.Arrays;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.impl.conn.DefaultRoutePlanner;
import org.apache.http.protocol.HttpContext;
import org.dspace.services.ConfigurationService;
/**
* Extension of {@link DefaultRoutePlanner} that determine the proxy based on
* the configuration service, ignoring configured hosts.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class DSpaceProxyRoutePlanner extends DefaultRoutePlanner {
private ConfigurationService configurationService;
public DSpaceProxyRoutePlanner(ConfigurationService configurationService) {
super(null);
this.configurationService = configurationService;
}
@Override
protected HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
if (isTargetHostConfiguredToBeIgnored(target)) {
return null;
}
String proxyHost = configurationService.getProperty("http.proxy.host");
String proxyPort = configurationService.getProperty("http.proxy.port");
if (StringUtils.isAnyBlank(proxyHost, proxyPort)) {
return null;
}
try {
return new HttpHost(proxyHost, Integer.parseInt(proxyPort), "http");
} catch (NumberFormatException e) {
throw new RuntimeException("Invalid proxy port configuration: " + proxyPort);
}
}
private boolean isTargetHostConfiguredToBeIgnored(HttpHost target) {
String[] hostsToIgnore = configurationService.getArrayProperty("http.proxy.hosts-to-ignore");
if (ArrayUtils.isEmpty(hostsToIgnore)) {
return false;
}
return Arrays.stream(hostsToIgnore)
.anyMatch(host -> matchesHost(host, target.getHostName()));
}
private boolean matchesHost(String hostPattern, String hostName) {
if (hostName.equals(hostPattern)) {
return true;
} else if (hostPattern.startsWith("*")) {
return hostName.endsWith(StringUtils.removeStart(hostPattern, "*"));
} else if (hostPattern.endsWith("*")) {
return hostName.startsWith(StringUtils.removeEnd(hostPattern, "*"));
}
return false;
}
}

View File

@@ -18,9 +18,9 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.app.ldn.model.Notification;
import org.dspace.content.Item;
import org.dspace.core.Context;
@@ -34,21 +34,13 @@ public class SendLDNMessageAction implements LDNAction {
private static final Logger log = LogManager.getLogger(SendLDNMessageAction.class);
private CloseableHttpClient client = null;
private CloseableHttpClient client;
public SendLDNMessageAction() {
HttpClientBuilder builder = HttpClientBuilder.create();
client = builder
.disableAutomaticRetries()
.setMaxConnTotal(5)
.build();
}
public SendLDNMessageAction(CloseableHttpClient client) {
this();
if (client != null) {
this.client = client;
}
this.client = client;
}
@Override
@@ -66,9 +58,10 @@ public class SendLDNMessageAction implements LDNAction {
// NOTE: Github believes there is a "Potential server-side request forgery due to a user-provided value"
// This is a false positive because the LDN Service URL is configured by the user from DSpace.
// See the frontend configuration at [dspace.ui.url]/admin/ldn/services
try (
CloseableHttpResponse response = client.execute(httpPost);
) {
if (client == null) {
client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5);
}
try (CloseableHttpResponse response = client.execute(httpPost)) {
if (isSuccessful(response.getStatusLine().getStatusCode())) {
result = LDNActionStatus.CONTINUE;
} else if (isRedirect(response.getStatusLine().getStatusCode())) {
@@ -77,6 +70,7 @@ public class SendLDNMessageAction implements LDNAction {
} catch (Exception e) {
log.error(e);
}
client.close();
return result;
}
@@ -91,9 +85,9 @@ public class SendLDNMessageAction implements LDNAction {
statusCode == HttpStatus.SC_TEMPORARY_REDIRECT;
}
private LDNActionStatus handleRedirect(CloseableHttpResponse oldresponse,
private LDNActionStatus handleRedirect(CloseableHttpResponse oldResponse,
HttpPost request) throws HttpException {
Header[] urls = oldresponse.getHeaders(HttpHeaders.LOCATION);
Header[] urls = oldResponse.getHeaders(HttpHeaders.LOCATION);
String url = urls.length > 0 && urls[0] != null ? urls[0].getValue() : null;
if (url == null) {
throw new HttpException("Error following redirect, unable to reach"
@@ -102,17 +96,14 @@ public class SendLDNMessageAction implements LDNAction {
LDNActionStatus result = LDNActionStatus.ABORT;
try {
request.setURI(new URI(url));
try (
CloseableHttpResponse response = client.execute(request);
) {
try (CloseableHttpResponse response = client.execute(request)) {
if (isSuccessful(response.getStatusLine().getStatusCode())) {
return LDNActionStatus.CONTINUE;
result = LDNActionStatus.CONTINUE;
}
}
} catch (Exception e) {
log.error("Error following redirect:", e);
}
return LDNActionStatus.ABORT;
return result;
}
}

View File

@@ -17,15 +17,15 @@ import jakarta.annotation.PostConstruct;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
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.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.app.sherpa.v2.SHERPAPublisherResponse;
import org.dspace.app.sherpa.v2.SHERPAResponse;
import org.dspace.app.sherpa.v2.SHERPAUtils;
@@ -45,8 +45,6 @@ import org.springframework.cache.annotation.Cacheable;
*/
public class SHERPAService {
private CloseableHttpClient client = null;
private int maxNumberOfTries;
private long sleepBetweenTimeouts;
private int timeout = 5000;
@@ -59,19 +57,6 @@ public class SHERPAService {
@Autowired
ConfigurationService configurationService;
/**
* Create a new HTTP builder with sensible defaults in constructor
*/
public SHERPAService() {
HttpClientBuilder builder = HttpClientBuilder.create();
// httpclient 4.3+ doesn't appear to have any sensible defaults any more. Setting conservative defaults as
// not to hammer the SHERPA service too much.
client = builder
.disableAutomaticRetries()
.setMaxConnTotal(5)
.build();
}
/**
* Complete initialization of the Bean.
*/
@@ -132,46 +117,47 @@ public class SHERPAService {
timeout,
sleepBetweenTimeouts));
try {
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) {
Thread.sleep(sleepBetweenTimeouts);
// Construct a default HTTP method (first result)
method = constructHttpGet(type, field, predicate, value, start, limit);
// Execute the method
HttpResponse response = client.execute(method);
int statusCode = response.getStatusLine().getStatusCode();
try (CloseableHttpResponse response = client.execute(method)) {
int statusCode = response.getStatusLine().getStatusCode();
log.debug(response.getStatusLine().getStatusCode() + ": "
+ response.getStatusLine().getReasonPhrase());
log.debug(response.getStatusLine().getStatusCode() + ": "
+ response.getStatusLine().getReasonPhrase());
if (statusCode != HttpStatus.SC_OK) {
sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: "
+ statusCode);
String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.error("Error from SHERPA HTTP request: " + errorBody);
}
HttpEntity responseBody = response.getEntity();
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
if (null != responseBody) {
log.debug("Non-null SHERPA response received for query of " + value);
InputStream content = null;
try {
content = responseBody.getContent();
sherpaResponse =
new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON);
} catch (IOException e) {
log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e);
} finally {
if (content != null) {
content.close();
}
if (statusCode != HttpStatus.SC_OK) {
sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: "
+ statusCode);
String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.error("Error from SHERPA HTTP request: " + errorBody);
}
HttpEntity responseBody = response.getEntity();
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
if (null != responseBody) {
log.debug("Non-null SHERPA response received for query of " + value);
InputStream content = null;
try {
content = responseBody.getContent();
sherpaResponse =
new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON);
} catch (IOException e) {
log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e);
} finally {
if (content != null) {
content.close();
}
}
} else {
log.debug("Empty SHERPA response body for query on " + value);
sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response");
}
} else {
log.debug("Empty SHERPA response body for query on " + value);
sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response");
}
} catch (URISyntaxException e) {
String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage();
@@ -235,45 +221,46 @@ public class SHERPAService {
timeout,
sleepBetweenTimeouts));
try {
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) {
Thread.sleep(sleepBetweenTimeouts);
// Construct a default HTTP method (first result)
method = constructHttpGet(type, field, predicate, value, start, limit);
// Execute the method
HttpResponse response = client.execute(method);
int statusCode = response.getStatusLine().getStatusCode();
try (CloseableHttpResponse response = client.execute(method)) {
int statusCode = response.getStatusLine().getStatusCode();
log.debug(response.getStatusLine().getStatusCode() + ": "
+ response.getStatusLine().getReasonPhrase());
log.debug(response.getStatusLine().getStatusCode() + ": "
+ response.getStatusLine().getReasonPhrase());
if (statusCode != HttpStatus.SC_OK) {
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: "
+ statusCode);
String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.error("Error from SHERPA HTTP request: " + errorBody);
}
HttpEntity responseBody = response.getEntity();
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
if (null != responseBody) {
log.debug("Non-null SHERPA response received for query of " + value);
InputStream content = null;
try {
content = responseBody.getContent();
sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON);
} catch (IOException e) {
log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e);
} finally {
if (content != null) {
content.close();
}
if (statusCode != HttpStatus.SC_OK) {
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: "
+ statusCode);
String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.error("Error from SHERPA HTTP request: " + errorBody);
}
HttpEntity responseBody = response.getEntity();
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
if (null != responseBody) {
log.debug("Non-null SHERPA response received for query of " + value);
InputStream content = null;
try {
content = responseBody.getContent();
sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON);
} catch (IOException e) {
log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e);
} finally {
if (content != null) {
content.close();
}
}
} else {
log.debug("Empty SHERPA response body for query on " + value);
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response");
}
} else {
log.debug("Empty SHERPA response body for query on " + value);
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response");
}
} catch (URISyntaxException e) {
String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage();
@@ -283,7 +270,7 @@ public class SHERPAService {
String errorMessage = "Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage();
log.error(errorMessage, e);
sherpaResponse = new SHERPAResponse(errorMessage);
} catch (InterruptedException e) {
} catch (InterruptedException e) {
String errorMessage = "Encountered exception while sleeping thread: " + e.getMessage();
log.error(errorMessage, e);
sherpaResponse = new SHERPAResponse(errorMessage);

View File

@@ -13,12 +13,12 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.app.util.dao.WebAppDAO;
import org.dspace.app.util.service.WebAppService;
import org.dspace.core.Context;
@@ -77,8 +77,8 @@ public class WebAppServiceImpl implements WebAppService {
for (WebApp app : webApps) {
method = new HttpHead(app.getUrl());
int status;
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
HttpResponse response = client.execute(method);
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
CloseableHttpResponse response = client.execute(method);
status = response.getStatusLine().getStatusCode();
}
if (status != HttpStatus.SC_OK) {

View File

@@ -22,12 +22,13 @@ import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.authenticate.oidc.OidcClient;
import org.dspace.authenticate.oidc.OidcClientException;
import org.dspace.authenticate.oidc.model.OidcTokenResponseDTO;
@@ -83,21 +84,17 @@ public class OidcClientImpl implements OidcClient {
}
private <T> T executeAndParseJson(HttpUriRequest httpUriRequest, Class<T> clazz) {
HttpClient client = HttpClientBuilder.create().build();
return executeAndReturns(() -> {
HttpResponse response = client.execute(httpUriRequest);
if (isNotSuccessfull(response)) {
throw new OidcClientException(getStatusCode(response), formatErrorMessage(response));
}
return objectMapper.readValue(getContent(response), clazz);
});
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
return executeAndReturns(() -> {
CloseableHttpResponse response = client.execute(httpUriRequest);
if (isNotSuccessfull(response)) {
throw new OidcClientException(getStatusCode(response), formatErrorMessage(response));
}
return objectMapper.readValue(getContent(response), clazz);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private <T> T executeAndReturns(ThrowingSupplier<T, Exception> supplier) {

View File

@@ -11,8 +11,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
@@ -21,7 +19,11 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
@@ -1310,13 +1312,12 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
if (params.getBooleanProperty("manifestOnly", false)) {
// NOTE: since we are only dealing with a METS manifest,
// we will assume all external files are available via URLs.
try {
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
// attempt to open a connection to given URL
URL fileURL = new URL(path);
URLConnection connection = fileURL.openConnection();
// open stream to access file contents
return connection.getInputStream();
try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path))) {
// open stream to access file contents
return httpResponse.getEntity().getContent();
}
} catch (IOException io) {
log
.error("Unable to retrieve external file from URL '"

View File

@@ -9,11 +9,15 @@ package org.dspace.ctask.general;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
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.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
@@ -135,24 +139,20 @@ public class BasicLinkChecker extends AbstractCurationTask {
* @return The HTTP response code (e.g. 200 / 301 / 404 / 500)
*/
protected int getResponseStatus(String url, int redirects) {
try {
URL theURL = new URL(url);
HttpURLConnection connection = (HttpURLConnection) theURL.openConnection();
connection.setInstanceFollowRedirects(true);
int statusCode = connection.getResponseCode();
RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build();
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) {
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url));
int statusCode = httpResponse.getStatusLine().getStatusCode();
int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0);
if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM ||
statusCode == HttpURLConnection.HTTP_SEE_OTHER)) {
connection.disconnect();
String newUrl = connection.getHeaderField("Location");
String newUrl = httpResponse.getFirstHeader("Location").getValue();
if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) {
redirects++;
return getResponseStatus(newUrl, redirects);
}
}
return statusCode;
} catch (IOException ioe) {
// Must be a bad URL
log.debug("Bad link: " + ioe.getMessage());

View File

@@ -30,13 +30,13 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
@@ -255,53 +255,50 @@ public class MetadataWebService extends AbstractCurationTask implements Namespac
}
protected int callService(String value, Item item, StringBuilder resultSb) throws IOException {
String callUrl = urlTemplate.replaceAll("\\{" + templateParam + "\\}", value);
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet req = new HttpGet(callUrl);
for (Map.Entry<String, String> entry : headers.entrySet()) {
req.addHeader(entry.getKey(), entry.getValue());
}
HttpResponse resp = client.execute(req);
int status = Curator.CURATE_ERROR;
int statusCode = resp.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity entity = resp.getEntity();
if (entity != null) {
// boiler-plate handling taken from Apache 4.1 javadoc
InputStream instream = entity.getContent();
try {
// This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD
// parsing during initialization of docBuilder in init()
Document doc = docBuilder.parse(instream); // lgtm [java/xxe]
status = processResponse(doc, item, resultSb);
} catch (SAXException saxE) {
log.error("caught exception: " + saxE);
resultSb.append(" unable to read response document");
} catch (RuntimeException ex) {
// In case of an unexpected exception you may want to abort
// the HTTP request in order to shut down the underlying
// connection and release it back to the connection manager.
req.abort();
log.error("caught exception: " + ex);
throw ex;
} finally {
// Closing the input stream will trigger connection release
instream.close();
}
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
client.close();
} else {
log.error(" obtained no valid service response");
resultSb.append("no service response");
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
HttpGet req = new HttpGet(callUrl);
for (Map.Entry<String, String> entry : headers.entrySet()) {
req.addHeader(entry.getKey(), entry.getValue());
}
try (CloseableHttpResponse resp = client.execute(req)) {
int status = Curator.CURATE_ERROR;
int statusCode = resp.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity entity = resp.getEntity();
if (entity != null) {
// boiler-plate handling taken from Apache 4.1 javadoc
InputStream instream = entity.getContent();
try {
// This next line triggers a false-positive XXE warning from LGTM, even though
// we disallow DTD parsing during initialization of docBuilder in init()
Document doc = docBuilder.parse(instream); // lgtm [java/xxe]
status = processResponse(doc, item, resultSb);
} catch (SAXException saxE) {
log.error("caught exception: " + saxE);
resultSb.append(" unable to read response document");
} catch (RuntimeException ex) {
// In case of an unexpected exception you may want to abort
// the HTTP request in order to shut down the underlying
// connection and release it back to the connection manager.
req.abort();
log.error("caught exception: " + ex);
throw ex;
} finally {
// Closing the input stream will trigger connection release
instream.close();
}
} else {
log.error(" obtained no valid service response");
resultSb.append("no service response");
}
} else {
log.error("service returned non-OK status: " + statusCode);
resultSb.append("no service response");
}
return status;
}
} else {
log.error("service returned non-OK status: " + statusCode);
resultSb.append("no service response");
}
return status;
}
protected int processResponse(Document doc, Item item, StringBuilder resultSb) throws IOException {

View File

@@ -12,12 +12,12 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
@@ -60,22 +60,20 @@ public class MicrosoftTranslator extends AbstractTranslator {
String url = baseUrl + "?appId=" + apiKey;
url += "&to=" + to + "&from=" + from + "&text=" + text;
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
HttpGet hm = new HttpGet(url);
HttpResponse httpResponse = client.execute(hm);
log.debug("Response code from API call is " + httpResponse);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
String response = IOUtils.toString(httpResponse.getEntity().getContent(),
StandardCharsets.ISO_8859_1);
response = response
.replaceAll("<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">", "");
response = response.replaceAll("</string>", "");
translatedText = response;
try (CloseableHttpResponse httpResponse = client.execute(hm)) {
log.debug("Response code from API call is " + httpResponse);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
String response = IOUtils.toString(httpResponse.getEntity().getContent(),
StandardCharsets.ISO_8859_1);
response = response
.replaceAll("<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">", "");
response = response.replaceAll("</string>", "");
translatedText = response;
}
}
}
return translatedText;
}
}
}

View File

@@ -17,15 +17,15 @@ import java.util.regex.Pattern;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.eperson.service.CaptchaService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -82,18 +82,17 @@ public class CaptchaServiceImpl implements CaptchaService {
throw new RuntimeException(e.getMessage(), e);
}
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse httpResponse;
GoogleCaptchaResponse googleResponse;
final ObjectMapper objectMapper = new ObjectMapper();
try {
httpResponse = httpClient.execute(httpPost);
googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), GoogleCaptchaResponse.class);
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
final ObjectMapper objectMapper = new ObjectMapper();
try (CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) {
GoogleCaptchaResponse googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(),
GoogleCaptchaResponse.class);
validateGoogleResponse(googleResponse, action);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new RuntimeException("Error during verify google recaptcha site", e);
}
validateGoogleResponse(googleResponse, action);
}
private boolean responseSanityCheck(String response) {

View File

@@ -27,13 +27,13 @@ import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.app.util.Util;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
@@ -120,33 +120,34 @@ public class OpenaireRestConnector {
params.add(new BasicNameValuePair("grant_type", "client_credentials"));
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse getResponse = httpClient.execute(httpPost);
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
HttpResponse getResponse = httpClient.execute(httpPost);
JSONObject responseObject = null;
try (InputStream is = getResponse.getEntity().getContent();
BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
String inputStr;
// verify if we have basic json
while ((inputStr = streamReader.readLine()) != null && responseObject == null) {
if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")
&& inputStr.contains("expires_in")) {
try {
responseObject = new JSONObject(inputStr);
} catch (Exception e) {
// Not as valid as I'd hoped, move along
responseObject = null;
JSONObject responseObject = null;
try (InputStream is = getResponse.getEntity().getContent();
BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
String inputStr;
// verify if we have basic json
while ((inputStr = streamReader.readLine()) != null && responseObject == null) {
if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")
&& inputStr.contains("expires_in")) {
try {
responseObject = new JSONObject(inputStr);
} catch (Exception e) {
// Not as valid as I'd hoped, move along
responseObject = null;
}
}
}
}
}
if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) {
throw new IOException("Unable to grab the access token using provided service url, client id and secret");
}
return new OpenaireRestToken(responseObject.get("access_token").toString(),
Long.valueOf(responseObject.get("expires_in").toString()));
if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) {
throw new IOException("Unable to grab the access token using provided service url, " +
"client id and secret");
}
return new OpenaireRestToken(responseObject.get("access_token").toString(),
Long.valueOf(responseObject.get("expires_in").toString()));
}
}
/**
@@ -171,42 +172,43 @@ public class OpenaireRestConnector {
httpGet.addHeader("Authorization", "Bearer " + accessToken);
}
HttpClient httpClient = HttpClientBuilder.create().build();
getResponse = httpClient.execute(httpGet);
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
getResponse = httpClient.execute(httpGet);
StatusLine status = getResponse.getStatusLine();
StatusLine status = getResponse.getStatusLine();
// registering errors
switch (status.getStatusCode()) {
case HttpStatus.SC_NOT_FOUND:
// 404 - Not found
case HttpStatus.SC_FORBIDDEN:
// 403 - Invalid Access Token
case 429:
// 429 - Rate limit abuse for unauthenticated user
Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used");
Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit");
// registering errors
switch (status.getStatusCode()) {
case HttpStatus.SC_NOT_FOUND:
// 404 - Not found
case HttpStatus.SC_FORBIDDEN:
// 403 - Invalid Access Token
case 429:
// 429 - Rate limit abuse for unauthenticated user
Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used");
Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit");
if (limitUsed.length > 0) {
String limitMsg = limitUsed[0].getValue();
if (limitMax.length > 0) {
limitMsg = limitMsg.concat(" of " + limitMax[0].getValue());
if (limitUsed.length > 0) {
String limitMsg = limitUsed[0].getValue();
if (limitMax.length > 0) {
limitMsg = limitMsg.concat(" of " + limitMax[0].getValue());
}
getGotError(new NoHttpResponseException(status.getReasonPhrase() + " with usage limit "
+ limitMsg),
url + '/' + file);
} else {
// 429 - Rate limit abuse
getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file);
}
getGotError(
new NoHttpResponseException(status.getReasonPhrase() + " with usage limit " + limitMsg),
url + '/' + file);
} else {
// 429 - Rate limit abuse
getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file);
}
break;
default:
// 200 or other
break;
}
break;
default:
// 200 or other
break;
}
// do not close this httpClient
result = getResponse.getEntity().getContent();
// do not close this httpClient
result = getResponse.getEntity().getContent();
}
} catch (MalformedURLException e1) {
getGotError(e1, url + '/' + file);
} catch (Exception e) {

View File

@@ -7,17 +7,18 @@
*/
package org.dspace.external;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
/**
* @author Antoine Snyers (antoine at atmire.com)
@@ -39,7 +40,7 @@ public class OrcidRestConnector {
}
public InputStream get(String path, String accessToken) {
HttpResponse getResponse = null;
CloseableHttpResponse getResponse = null;
InputStream result = null;
path = trimSlashes(path);
@@ -49,11 +50,13 @@ public class OrcidRestConnector {
httpGet.addHeader("Content-Type", "application/vnd.orcid+xml");
httpGet.addHeader("Authorization","Bearer " + accessToken);
}
try {
HttpClient httpClient = HttpClientBuilder.create().build();
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
getResponse = httpClient.execute(httpGet);
//do not close this httpClient
result = getResponse.getEntity().getContent();
try (InputStream responseStream = getResponse.getEntity().getContent()) {
// Read all the content of the response stream into a byte array to prevent TruncatedChunkException
byte[] content = responseStream.readAllBytes();
result = new ByteArrayInputStream(content);
}
} catch (Exception e) {
getGotError(e, fullPath);
}

View File

@@ -169,13 +169,7 @@ public class OrcidV3AuthorDataProvider extends AbstractExternalDataProvider {
}
initializeAccessToken();
InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken);
Person person = converter.convertSinglePerson(bioDocument);
try {
bioDocument.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return person;
return converter.convertSinglePerson(bioDocument);
}
/**
@@ -220,11 +214,6 @@ public class OrcidV3AuthorDataProvider extends AbstractExternalDataProvider {
}
}
}
try {
bioDocument.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList());
}

View File

@@ -18,9 +18,9 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.google.GoogleAnalyticsEvent;
/**
@@ -42,7 +42,7 @@ public class GoogleAnalyticsClientImpl implements GoogleAnalyticsClient {
public GoogleAnalyticsClientImpl(String keyPrefix, GoogleAnalyticsClientRequestBuilder requestBuilder) {
this.keyPrefix = keyPrefix;
this.requestBuilder = requestBuilder;
this.httpclient = HttpClients.createDefault();
this.httpclient = DSpaceHttpClientFactory.getInstance().build();
}
@Override

View File

@@ -36,10 +36,10 @@ import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.content.crosswalk.CrosswalkException;
@@ -719,7 +719,7 @@ public class DataCiteConnector
httpContext.setCredentialsProvider(credentialsProvider);
HttpEntity entity = null;
try ( CloseableHttpClient httpclient = HttpClientBuilder.create().build(); ) {
try (CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().build()) {
HttpResponse response = httpclient.execute(req, httpContext);
StatusLine status = response.getStatusLine();

View File

@@ -26,9 +26,9 @@ import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.identifier.DOI;
import org.dspace.identifier.IdentifierException;
@@ -87,7 +87,7 @@ public class EZIDRequest {
this.authority = authority;
}
client = HttpClientBuilder.create().build();
client = DSpaceHttpClientFactory.getInstance().build();
httpContext = HttpClientContext.create();
if (null != username) {
URI uri = new URI(scheme, host, path, null);
@@ -124,7 +124,7 @@ public class EZIDRequest {
this.authority = authority;
}
client = HttpClientBuilder.create().build();
client = DSpaceHttpClientFactory.getInstance().build();
httpContext = HttpClientContext.create();
if (null != username) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();

View File

@@ -12,12 +12,14 @@ import static org.dspace.iiif.canvasdimension.Util.checkDimensions;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.content.Bitstream;
import org.dspace.iiif.util.IIIFSharedUtils;
@@ -35,14 +37,10 @@ public class IIIFApiQueryServiceImpl implements IIIFApiQueryService {
public int[] getImageDimensions(Bitstream bitstream) {
int[] arr = new int[2];
String path = IIIFSharedUtils.getInfoJsonPath(bitstream);
URL url;
BufferedReader in = null;
try {
url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path));
in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {

View File

@@ -17,19 +17,16 @@ import java.util.Optional;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -53,16 +50,11 @@ public class LiveImportClientImpl implements LiveImportClient {
@Override
public String executeHttpGetRequest(int timeout, String URL, Map<String, Map<String, String>> params) {
HttpGet method = null;
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(timeout).build();
try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient)
.orElseGet(HttpClients::createDefault)) {
Builder requestConfigBuilder = RequestConfig.custom();
requestConfigBuilder.setConnectionRequestTimeout(timeout);
RequestConfig defaultRequestConfig = requestConfigBuilder.build();
.orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) {
String uri = buildUrl(URL, params.get(URI_PARAMETERS));
method = new HttpGet(uri);
method.setConfig(defaultRequestConfig);
Map<String, String> headerParams = params.get(HEADER_PARAMETERS);
if (MapUtils.isNotEmpty(headerParams)) {
@@ -71,7 +63,6 @@ public class LiveImportClientImpl implements LiveImportClient {
}
}
configureProxy(method, defaultRequestConfig);
if (log.isDebugEnabled()) {
log.debug("Performing GET request to \"" + uri + "\"...");
}
@@ -95,21 +86,17 @@ public class LiveImportClientImpl implements LiveImportClient {
@Override
public String executeHttpPostRequest(String URL, Map<String, Map<String, String>> params, String entry) {
HttpPost method = null;
RequestConfig config = RequestConfig.custom().build();
try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient)
.orElseGet(HttpClients::createDefault)) {
Builder requestConfigBuilder = RequestConfig.custom();
RequestConfig defaultRequestConfig = requestConfigBuilder.build();
.orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) {
String uri = buildUrl(URL, params.get(URI_PARAMETERS));
method = new HttpPost(uri);
method.setConfig(defaultRequestConfig);
if (StringUtils.isNotBlank(entry)) {
method.setEntity(new StringEntity(entry));
}
setHeaderParams(method, params);
configureProxy(method, defaultRequestConfig);
if (log.isDebugEnabled()) {
log.debug("Performing POST request to \"" + uri + "\"..." );
}
@@ -129,17 +116,6 @@ public class LiveImportClientImpl implements LiveImportClient {
return StringUtils.EMPTY;
}
private void configureProxy(HttpRequestBase method, RequestConfig defaultRequestConfig) {
String proxyHost = configurationService.getProperty("http.proxy.host");
String proxyPort = configurationService.getProperty("http.proxy.port");
if (StringUtils.isNotBlank(proxyHost) && StringUtils.isNotBlank(proxyPort)) {
RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig)
.setProxy(new HttpHost(proxyHost, Integer.parseInt(proxyPort), "http"))
.build();
method.setConfig(requestConfig);
}
}
/**
* Allows to set the header parameters to the HTTP Post method
*

View File

@@ -10,9 +10,6 @@ package org.dspace.license;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
@@ -28,9 +25,9 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.services.ConfigurationService;
import org.jdom2.Attribute;
import org.jdom2.Document;
@@ -70,12 +67,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService,
@Override
public void afterPropertiesSet() throws Exception {
HttpClientBuilder builder = HttpClientBuilder.create();
client = builder
.disableAutomaticRetries()
.setMaxConnTotal(5)
.build();
client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5);
// disallow DTD parsing to ensure no XXE attacks can occur.
// See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
@@ -333,23 +325,13 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService,
@Override
public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException {
String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl");
String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI;
URL request_url;
try {
request_url = new URL(issueUrl);
} catch (MalformedURLException e) {
return null;
}
URLConnection connection = request_url.openConnection();
connection.setDoOutput(true);
try {
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
CloseableHttpResponse httpResponse = httpClient.execute(new HttpPost(issueUrl));
// parsing document from input stream
InputStream stream = connection.getInputStream();
InputStream stream = httpResponse.getEntity().getContent();
Document doc = parser.build(stream);
return doc;
} catch (Exception e) {
log.error("Error while retrieving the license document for URI: " + licenseURI, e);
}

View File

@@ -35,13 +35,14 @@ import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.orcid.OrcidToken;
import org.dspace.orcid.exception.OrcidClientException;
import org.dspace.orcid.model.OrcidEntityType;
@@ -254,10 +255,8 @@ public class OrcidClientImpl implements OrcidClient {
}
private void executeSuccessful(HttpUriRequest httpUriRequest) {
try {
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(httpUriRequest);
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
CloseableHttpResponse response = client.execute(httpUriRequest);
if (isNotSuccessfull(response)) {
throw new OrcidClientException(
getStatusCode(response),
@@ -272,21 +271,17 @@ public class OrcidClientImpl implements OrcidClient {
}
private <T> T executeAndParseJson(HttpUriRequest httpUriRequest, Class<T> clazz) {
HttpClient client = HttpClientBuilder.create().build();
return executeAndReturns(() -> {
HttpResponse response = client.execute(httpUriRequest);
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return objectMapper.readValue(response.getEntity().getContent(), clazz);
});
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
return executeAndReturns(() -> {
CloseableHttpResponse response = client.execute(httpUriRequest);
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return objectMapper.readValue(response.getEntity().getContent(), clazz);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
@@ -301,44 +296,37 @@ public class OrcidClientImpl implements OrcidClient {
* @throws OrcidClientException if the incoming response is not successful
*/
private <T> T executeAndUnmarshall(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull, Class<T> clazz) {
HttpClient client = HttpClientBuilder.create().build();
return executeAndReturns(() -> {
HttpResponse response = client.execute(httpUriRequest);
if (handleNotFoundAsNull && isNotFound(response)) {
return null;
}
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return unmarshall(response.getEntity(), clazz);
});
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
return executeAndReturns(() -> {
CloseableHttpResponse response = client.execute(httpUriRequest);
if (handleNotFoundAsNull && isNotFound(response)) {
return null;
}
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return unmarshall(response.getEntity(), clazz);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private OrcidResponse execute(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull) {
HttpClient client = HttpClientBuilder.create().build();
return executeAndReturns(() -> {
HttpResponse response = client.execute(httpUriRequest);
if (handleNotFoundAsNull && isNotFound(response)) {
return new OrcidResponse(getStatusCode(response), null, getContent(response));
}
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response));
});
try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) {
return executeAndReturns(() -> {
CloseableHttpResponse response = client.execute(httpUriRequest);
if (handleNotFoundAsNull && isNotFound(response)) {
return new OrcidResponse(getStatusCode(response), null, getContent(response));
}
if (isNotSuccessfull(response)) {
throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response));
}
return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response));
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private <T> T executeAndReturns(ThrowingSupplier<T, Exception> supplier) {

View File

@@ -20,7 +20,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.json.JSONObject;
/**
@@ -97,7 +97,7 @@ public final class OrcidFactoryUtils {
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpResponse response;
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) {
response = httpClient.execute(httpPost);
}
JSONObject responseObject = null;

View File

@@ -22,9 +22,9 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.content.Item;
import org.dspace.content.QAEvent;
import org.dspace.content.service.ItemService;
@@ -114,20 +114,19 @@ public class QAEventActionServiceImpl implements QAEventActionService {
* Make acknowledgement to the configured urls for the event status.
*/
private void makeAcknowledgement(String eventId, String source, String status) {
String[] ackwnoledgeCallbacks = configurationService
String[] acknowledgeCallbacks = configurationService
.getArrayProperty("qaevents." + source + ".acknowledge-url");
if (ackwnoledgeCallbacks != null) {
for (String ackwnoledgeCallback : ackwnoledgeCallbacks) {
if (StringUtils.isNotBlank(ackwnoledgeCallback)) {
if (acknowledgeCallbacks != null) {
for (String acknowledgeCallback : acknowledgeCallbacks) {
if (StringUtils.isNotBlank(acknowledgeCallback)) {
ObjectNode node = jsonMapper.createObjectNode();
node.put("eventId", eventId);
node.put("status", status);
StringEntity requestEntity = new StringEntity(node.toString(), ContentType.APPLICATION_JSON);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost postMethod = new HttpPost(ackwnoledgeCallback);
postMethod.setEntity(requestEntity);
try {
httpclient.execute(postMethod);
try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) {
HttpPost postMethod = new HttpPost(acknowledgeCallback);
postMethod.setEntity(requestEntity);
httpClient.execute(postMethod);
} catch (IOException e) {
log.error(e.getMessage(), e);
}

View File

@@ -19,11 +19,11 @@ import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.services.ConfigurationService;
/**
@@ -112,7 +112,7 @@ public class HttpConnectionPoolService {
* @return the client.
*/
public CloseableHttpClient getClient() {
CloseableHttpClient httpClient = HttpClientBuilder.create()
CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(true).create()
.setKeepAliveStrategy(keepAliveStrategy)
.setConnectionManager(connManager)
.build();

View File

@@ -54,7 +54,6 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.SolrClient;
@@ -84,6 +83,7 @@ import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.util.NamedList;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
@@ -1208,7 +1208,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
+ "."
+ i
+ ".csv");
try ( CloseableHttpClient hc = HttpClientBuilder.create().build(); ) {
try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) {
HttpResponse response = hc.execute(get);
csvInputstream = response.getEntity().getContent();
//Write the csv output to a file !
@@ -1350,7 +1350,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
HttpGet get = new HttpGet(solrRequestUrl);
List<String[]> rows;
try ( CloseableHttpClient hc = HttpClientBuilder.create().build(); ) {
try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) {
HttpResponse response = hc.execute(get);
InputStream csvOutput = response.getEntity().getContent();
Reader csvReader = new InputStreamReader(csvOutput);

View File

@@ -15,13 +15,13 @@ import java.time.LocalDate;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
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.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.client.DSpaceHttpClientFactory;
import org.dspace.core.Context;
import org.dspace.statistics.export.OpenURLTracker;
import org.springframework.beans.factory.annotation.Autowired;
@@ -69,16 +69,16 @@ public class OpenUrlServiceImpl implements OpenUrlService {
* @throws IOException
*/
protected int getResponseCodeFromUrl(final String urlStr) throws IOException {
HttpGet httpGet = new HttpGet(urlStr);
HttpClient httpClient = getHttpClient(getHttpClientRequestConfig());
HttpResponse httpResponse = httpClient.execute(httpGet);
return httpResponse.getStatusLine().getStatusCode();
try (CloseableHttpClient httpClient = getHttpClient(getHttpClientRequestConfig())) {
HttpGet httpGet = new HttpGet(urlStr);
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
return httpResponse.getStatusLine().getStatusCode();
}
}
}
protected HttpClient getHttpClient(RequestConfig requestConfig) {
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.build();
protected CloseableHttpClient getHttpClient(RequestConfig requestConfig) {
return DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(requestConfig);
}
protected RequestConfig getHttpClientRequestConfig() {

View File

@@ -0,0 +1,256 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
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.conn.routing.HttpRoute;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HttpContext;
import org.dspace.services.ConfigurationService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link DSpaceHttpClientFactory}.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
@RunWith(MockitoJUnitRunner.class)
public class DSpaceHttpClientFactoryTest {
@InjectMocks
private DSpaceHttpClientFactory httpClientFactory;
@Mock
private ConfigurationService configurationService;
private MockWebServer mockProxy;
private MockWebServer mockServer;
@Before
public void init() {
this.httpClientFactory.setProxyRoutePlanner(new DSpaceProxyRoutePlanner(configurationService));
this.mockProxy = new MockWebServer();
this.mockProxy.enqueue(new MockResponse().setResponseCode(200).addHeader("From", "Proxy"));
this.mockServer = new MockWebServer();
this.mockServer.enqueue(new MockResponse().setResponseCode(200).addHeader("From", "Server"));
}
@Test
public void testBuildWithProxyConfigured() throws Exception {
setHttpProxyOnConfigurationService();
CloseableHttpClient httpClient = httpClientFactory.build();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Proxy"));
assertThat(mockProxy.getRequestCount(), is(1));
assertThat(mockServer.getRequestCount(), is(0));
RecordedRequest request = mockProxy.takeRequest(100, TimeUnit.MILLISECONDS);
assertThat(request, notNullValue());
assertThat(request.getRequestUrl(), is(mockProxy.url("")));
assertThat(request.getRequestLine(), is("GET " + mockServer.url("").toString() + " HTTP/1.1"));
verify(configurationService).getProperty("http.proxy.host");
verify(configurationService).getProperty("http.proxy.port");
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithProxyConfiguredAndHostToIgnoreSet() throws Exception {
setHttpProxyOnConfigurationService(mockServer.getHostName());
CloseableHttpClient httpClient = httpClientFactory.build();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server"));
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(1));
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithProxyConfiguredAndHostPrefixToIgnoreSet() throws Exception {
setHttpProxyOnConfigurationService("local*", "www.test.com");
CloseableHttpClient httpClient = httpClientFactory.build();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server"));
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(1));
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithProxyConfiguredAndHostSuffixToIgnoreSet() throws Exception {
setHttpProxyOnConfigurationService("www.test.com", "*host");
CloseableHttpClient httpClient = httpClientFactory.build();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server"));
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(1));
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithoutConfiguredProxy() throws Exception {
CloseableHttpClient httpClient = httpClientFactory.build();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server"));
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(1));
RecordedRequest request = mockServer.takeRequest(100, TimeUnit.MILLISECONDS);
assertThat(request, notNullValue());
assertThat(request.getRequestUrl(), is(mockServer.url("")));
assertThat(request.getRequestLine(), is("GET / HTTP/1.1"));
verify(configurationService).getProperty("http.proxy.host");
verify(configurationService).getProperty("http.proxy.port");
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithoutProxy() throws Exception {
CloseableHttpClient httpClient = httpClientFactory.buildWithoutProxy();
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(0));
httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(mockServer.getRequestCount(), is(1));
assertThat(mockProxy.getRequestCount(), is(0));
RecordedRequest request = mockServer.takeRequest(100, TimeUnit.MILLISECONDS);
assertThat(request, notNullValue());
assertThat(request.getRequestUrl(), is(mockServer.url("")));
assertThat(request.getRequestLine(), is("GET / HTTP/1.1"));
verifyNoInteractions(configurationService);
}
@Test
public void testBuildWithoutAutomaticRetries() throws Exception {
setHttpProxyOnConfigurationService("www.test.com");
CloseableHttpClient httpClient = httpClientFactory.buildWithoutAutomaticRetries(10);
httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(mockProxy.getRequestCount(), is(1));
assertThat(mockServer.getRequestCount(), is(0));
RecordedRequest request = mockProxy.takeRequest(100, TimeUnit.MILLISECONDS);
assertThat(request, notNullValue());
assertThat(request.getRequestUrl(), is(mockProxy.url("")));
assertThat(request.getRequestLine(), is("GET " + mockServer.url("").toString() + " HTTP/1.1"));
verify(configurationService).getProperty("http.proxy.host");
verify(configurationService).getProperty("http.proxy.port");
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
@Test
public void testBuildWithHttpRequestInterceptor() throws Exception {
setHttpProxyOnConfigurationService("*test.com", "www.dspace.com");
AtomicReference<HttpContext> contextReference = new AtomicReference<HttpContext>();
HttpRequestInterceptor interceptor = (request, context) -> contextReference.set(context);
httpClientFactory.setRequestInterceptors(List.of(interceptor));
CloseableHttpClient httpClient = httpClientFactory.build();
httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(mockProxy.getRequestCount(), is(1));
assertThat(mockServer.getRequestCount(), is(0));
HttpContext httpContext = contextReference.get();
assertThat(httpContext, notNullValue());
Object httpRouteObj = httpContext.getAttribute("http.route");
assertThat(httpRouteObj, notNullValue());
assertThat(httpRouteObj, instanceOf(HttpRoute.class));
HttpRoute httpRoute = (HttpRoute) httpRouteObj;
assertThat(httpRoute.getHopCount(), is(2));
assertThat(httpRoute.getHopTarget(0).getPort(), is(mockProxy.getPort()));
assertThat(httpRoute.getHopTarget(1).getPort(), is(mockServer.getPort()));
}
@Test
public void testBuildWithHttpResponseInterceptor() throws Exception {
AtomicReference<HttpResponse> responseReference = new AtomicReference<HttpResponse>();
HttpResponseInterceptor responseInterceptor = (response, context) -> responseReference.set(response);
httpClientFactory.setResponseInterceptors(List.of(responseInterceptor));
CloseableHttpClient httpClient = httpClientFactory.build();
httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(mockProxy.getRequestCount(), is(0));
assertThat(mockServer.getRequestCount(), is(1));
HttpResponse httpResponse = responseReference.get();
assertThat(httpResponse, notNullValue());
assertThat(httpResponse.getHeaders("From"), arrayWithSize(1));
assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server"));
}
@Test
public void testBuildWithRequestConfig() throws Exception {
setHttpProxyOnConfigurationService();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(2500)
.build();
AtomicReference<HttpContext> contextReference = new AtomicReference<HttpContext>();
HttpRequestInterceptor interceptor = (request, context) -> contextReference.set(context);
httpClientFactory.setRequestInterceptors(List.of(interceptor));
CloseableHttpClient httpClient = httpClientFactory.buildWithRequestConfig(requestConfig);
httpClient.execute(new HttpGet(mockServer.url("").toString()));
assertThat(mockProxy.getRequestCount(), is(1));
assertThat(mockServer.getRequestCount(), is(0));
HttpContext httpContext = contextReference.get();
assertThat(httpContext, notNullValue());
Object httpRequestConfigObj = httpContext.getAttribute("http.request-config");
assertThat(httpRequestConfigObj, notNullValue());
assertThat(httpRequestConfigObj, instanceOf(RequestConfig.class));
assertThat(((RequestConfig) httpRequestConfigObj).getConnectTimeout(), is(2500));
verify(configurationService).getProperty("http.proxy.host");
verify(configurationService).getProperty("http.proxy.port");
verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore");
verifyNoMoreInteractions(configurationService);
}
private void setHttpProxyOnConfigurationService(String... hostsToIgnore) {
when(configurationService.getProperty("http.proxy.host")).thenReturn(mockProxy.getHostName());
when(configurationService.getProperty("http.proxy.port")).thenReturn(String.valueOf(mockProxy.getPort()));
when(configurationService.getArrayProperty("http.proxy.hosts-to-ignore")).thenReturn(hostsToIgnore);
}
}

View File

@@ -25,9 +25,9 @@ import java.sql.SQLException;
import java.time.LocalDate;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.dspace.core.Context;
import org.dspace.statistics.export.OpenURLTracker;
import org.junit.Before;
@@ -52,7 +52,7 @@ public class OpenUrlServiceImplTest {
private FailedOpenURLTrackerService failedOpenURLTrackerService;
@Mock
private HttpClient httpClient;
private CloseableHttpClient httpClient;
@Before
public void setUp() throws Exception {
@@ -71,11 +71,11 @@ public class OpenUrlServiceImplTest {
* @param statusCode the http status code to use in the mock.
* @return a mocked http response.
*/
protected HttpResponse createMockHttpResponse(int statusCode) {
protected CloseableHttpResponse createMockHttpResponse(int statusCode) {
StatusLine statusLine = mock(StatusLine.class);
when(statusLine.getStatusCode()).thenReturn(statusCode);
HttpResponse httpResponse = mock(HttpResponse.class);
CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
return httpResponse;

View File

@@ -411,6 +411,8 @@ http.proxy.host =
# port number of proxy server
http.proxy.port =
http.proxy.hosts-to-ignore = 127.0.0.1, localhost
# If enabled, the logging and the Solr statistics system will look for an X-Forwarded-For header.
# If it finds it, it will use this for the user IP address.
# NOTE: This is required to be enabled if you plan to use the Angular UI, as the server-side rendering provided in

View File

@@ -162,6 +162,9 @@
<bean class="org.dspace.authorize.ValidatePasswordServiceImpl"/>
<bean class="org.dspace.authorize.RegexPasswordValidator" />
<bean id="org.dspace.app.client.DSpaceHttpClientFactory" class="org.dspace.app.client.DSpaceHttpClientFactory"/>
<bean class="org.dspace.app.client.DSpaceProxyRoutePlanner"/>
<bean class="org.dspace.supervision.SupervisionOrderServiceImpl"/>
<bean class="org.dspace.content.ItemFilterServiceImpl"/>

View File

@@ -1746,6 +1746,12 @@
<version>2.3.232</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.12.0</version>
<scope>test</scope>
</dependency>
<!-- Findbugs annotations -->
<dependency>