Skip to content

Commit

Permalink
Null out references to java.net.http.HttpClient (#6630)
Browse files Browse the repository at this point in the history
HttpClient maintains a thread pool and a single
selector thread. These resources cannot be freed
if a strong reference to the HttpClient instance
exists.
  • Loading branch information
adutra authored Apr 19, 2023
1 parent 1c36e00 commit ee6f9d3
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
@SuppressWarnings("Since15") // IntelliJ warns about new APIs. 15 is misleading, it means 11
public final class JavaHttpClient implements org.projectnessie.client.http.HttpClient {
final HttpRuntimeConfig config;
final HttpClient client;
private HttpClient client;

public JavaHttpClient(HttpRuntimeConfig config) {
this.config = config;
Expand Down Expand Up @@ -65,7 +65,7 @@ public JavaHttpClient(HttpRuntimeConfig config) {

@Override
public HttpRequest newRequest() {
return new JavaRequest(this);
return new JavaRequest(this.config, (req, handler) -> client.send(req, handler));
}

@Override
Expand All @@ -75,6 +75,7 @@ public URI getBaseUri() {

@Override
public void close() {
client = null;
config.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.projectnessie.client.http.ResponseContext;
import org.projectnessie.client.http.impl.BaseHttpRequest;
import org.projectnessie.client.http.impl.HttpHeaders.HttpHeader;
import org.projectnessie.client.http.impl.HttpRuntimeConfig;
import org.projectnessie.client.http.impl.RequestContextImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -53,13 +54,31 @@
@SuppressWarnings("Since15") // IntelliJ warns about new APIs. 15 is misleading, it means 11
final class JavaRequest extends BaseHttpRequest {

/**
* A functional interface that is used to send an {@link HttpRequest} and return an {@link
* HttpResponse} without leaking the {@link HttpClient} instance.
*/
@FunctionalInterface
interface HttpExchange<T> {

/**
* Sends the given request using the underlying client, blocking if necessary to get the
* response. The returned {@link HttpResponse}{@code <T>} contains the response status, headers,
* and body (as handled by given response body handler).
*
* @see HttpClient#send(HttpRequest, HttpResponse.BodyHandler)
*/
HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler)
throws IOException, InterruptedException;
}

private static final Logger LOGGER = LoggerFactory.getLogger(JavaRequest.class);

private final HttpClient client;
private final HttpExchange<InputStream> exchange;

JavaRequest(JavaHttpClient client) {
super(client.config);
this.client = client.client;
JavaRequest(HttpRuntimeConfig config, HttpExchange<InputStream> exchange) {
super(config);
this.exchange = exchange;
}

@Override
Expand Down Expand Up @@ -88,7 +107,7 @@ public org.projectnessie.client.http.HttpResponse executeRequest(Method method,
try {
try {
LOGGER.debug("Sending {} request to {} ...", method, uri);
response = client.send(request.build(), BodyHandlers.ofInputStream());
response = exchange.send(request.build(), BodyHandlers.ofInputStream());
} catch (HttpConnectTimeoutException e) {
throw new HttpClientException(
String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void tearDown() throws Exception {
}
});
api().close();
api = null;
}
}

Expand Down

0 comments on commit ee6f9d3

Please sign in to comment.