Skip to content

Commit 5133244

Browse files
shawkinsmanusa
authored andcommitted
fix #4666: addressing okhttp sources needing explicit close
1 parent 243239e commit 5133244

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### 6.4-SNAPSHOT
44

55
#### Bugs
6+
* Fix #4666: fixed okhttp calls not explicitly closing
67

78
#### Improvements
89

httpclient-okhttp/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@
7979
<artifactId>mockwebserver</artifactId>
8080
<scope>test</scope>
8181
</dependency>
82+
<dependency>
83+
<groupId>org.mockito</groupId>
84+
<artifactId>mockito-inline</artifactId>
85+
</dependency>
8286
<dependency>
8387
<groupId>org.assertj</groupId>
8488
<artifactId>assertj-core</artifactId>

httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientImpl.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import java.util.Optional;
6363
import java.util.concurrent.CompletableFuture;
6464
import java.util.concurrent.ConcurrentHashMap;
65+
import java.util.concurrent.Executor;
6566
import java.util.concurrent.ExecutorService;
6667
import java.util.concurrent.RejectedExecutionException;
6768
import java.util.function.Function;
@@ -80,16 +81,18 @@ static MediaType parseMediaType(String contentType) {
8081
return result;
8182
}
8283

83-
private abstract class OkHttpAsyncBody<T> implements AsyncBody {
84+
abstract static class OkHttpAsyncBody<T> implements AsyncBody {
8485
private final AsyncBody.Consumer<T> consumer;
8586
private final BufferedSource source;
8687
private final CompletableFuture<Void> done = new CompletableFuture<>();
8788
private boolean consuming;
8889
private boolean requested;
90+
private Executor executor;
8991

90-
private OkHttpAsyncBody(AsyncBody.Consumer<T> consumer, BufferedSource source) {
92+
OkHttpAsyncBody(AsyncBody.Consumer<T> consumer, BufferedSource source, Executor executor) {
9193
this.consumer = consumer;
9294
this.source = source;
95+
this.executor = executor;
9396
}
9497

9598
@Override
@@ -103,7 +106,7 @@ public void consume() {
103106
}
104107
try {
105108
// consume should not block a caller, delegate to the dispatcher thread pool
106-
httpClient.dispatcher().executorService().execute(this::doConsume);
109+
executor.execute(this::doConsume);
107110
} catch (Exception e) {
108111
// executor is likely shutdown
109112
Utils.closeQuietly(source);
@@ -125,6 +128,8 @@ private void doConsume() {
125128
T value = process(source);
126129
consumer.consume(value, this);
127130
} else {
131+
// even if we've read everything an explicit close is still needed
132+
source.close();
128133
done.complete(null);
129134
}
130135
}
@@ -311,7 +316,8 @@ private okhttp3.Request.Builder newRequestBuilder() {
311316
@Override
312317
public CompletableFuture<HttpResponse<AsyncBody>> consumeBytesDirect(StandardHttpRequest request,
313318
Consumer<List<ByteBuffer>> consumer) {
314-
Function<BufferedSource, AsyncBody> handler = s -> new OkHttpAsyncBody<List<ByteBuffer>>(consumer, s) {
319+
Function<BufferedSource, AsyncBody> handler = s -> new OkHttpAsyncBody<List<ByteBuffer>>(consumer, s,
320+
this.httpClient.dispatcher().executorService()) {
315321
@Override
316322
protected List<ByteBuffer> process(BufferedSource source) throws IOException {
317323
// read only what is available otherwise okhttp will block trying to read

httpclient-okhttp/src/test/java/io/fabric8/kubernetes/client/okhttp/OkHttpAsyncBodyTest.java

+25
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,36 @@
1717

1818
import io.fabric8.kubernetes.client.http.AbstractAsyncBodyTest;
1919
import io.fabric8.kubernetes.client.http.HttpClient;
20+
import io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl.OkHttpAsyncBody;
21+
import okio.BufferedSource;
22+
import org.junit.jupiter.api.Test;
23+
import org.mockito.Mockito;
24+
25+
import java.io.IOException;
26+
import java.nio.ByteBuffer;
27+
import java.util.List;
2028

2129
@SuppressWarnings("java:S2187")
2230
public class OkHttpAsyncBodyTest extends AbstractAsyncBodyTest {
2331
@Override
2432
protected HttpClient.Factory getHttpClientFactory() {
2533
return new OkHttpClientFactory();
2634
}
35+
36+
@Test
37+
void testClosedWhenExhausted() throws Exception {
38+
BufferedSource source = Mockito.mock(BufferedSource.class);
39+
Mockito.when(source.exhausted()).thenReturn(true);
40+
OkHttpClientImpl.OkHttpAsyncBody<List<ByteBuffer>> asyncBody = new OkHttpAsyncBody<List<ByteBuffer>>(null, source,
41+
Runnable::run) {
42+
43+
@Override
44+
protected List<ByteBuffer> process(BufferedSource source) throws IOException {
45+
return null;
46+
}
47+
};
48+
49+
asyncBody.consume();
50+
Mockito.verify(source).close();
51+
}
2752
}

0 commit comments

Comments
 (0)