Skip to content

Commit e30ac34

Browse files
committed
test: added connection leakage test for OkHttp
Signed-off-by: Marc Nuri <[email protected]>
1 parent 5133244 commit e30ac34

File tree

6 files changed

+147
-29
lines changed

6 files changed

+147
-29
lines changed

CHANGELOG.md

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

55
#### Bugs
6-
* Fix #4666: fixed okhttp calls not explicitly closing
76

87
#### Improvements
98

@@ -13,6 +12,11 @@
1312

1413
#### _**Note**_: Breaking changes
1514

15+
### 6.3.1-SNAPSHOT
16+
17+
#### Bugs
18+
* Fix #4666: fixed okhttp calls not explicitly closing
19+
1620
### 6.3.0 (2022-12-12)
1721

1822
#### Bugs

httpclient-okhttp/pom.xml

+8-3
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,18 @@
7575
<scope>test</scope>
7676
</dependency>
7777
<dependency>
78-
<groupId>io.fabric8</groupId>
79-
<artifactId>mockwebserver</artifactId>
78+
<groupId>org.junit.jupiter</groupId>
79+
<artifactId>junit-jupiter-params</artifactId>
8080
<scope>test</scope>
8181
</dependency>
8282
<dependency>
8383
<groupId>org.mockito</groupId>
84-
<artifactId>mockito-inline</artifactId>
84+
<artifactId>mockito-core</artifactId>
85+
</dependency>
86+
<dependency>
87+
<groupId>io.fabric8</groupId>
88+
<artifactId>mockwebserver</artifactId>
89+
<scope>test</scope>
8590
</dependency>
8691
<dependency>
8792
<groupId>org.assertj</groupId>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ abstract static class OkHttpAsyncBody<T> implements AsyncBody {
8787
private final CompletableFuture<Void> done = new CompletableFuture<>();
8888
private boolean consuming;
8989
private boolean requested;
90-
private Executor executor;
90+
private final Executor executor;
9191

9292
OkHttpAsyncBody(AsyncBody.Consumer<T> consumer, BufferedSource source, Executor executor) {
9393
this.consumer = consumer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.okhttp;
17+
18+
import io.fabric8.kubernetes.client.http.AsyncBody;
19+
import io.fabric8.kubernetes.client.http.HttpClient;
20+
import io.fabric8.kubernetes.client.http.HttpResponse;
21+
import okhttp3.ConnectionPool;
22+
import okhttp3.Protocol;
23+
import okhttp3.mockwebserver.MockResponse;
24+
import okhttp3.mockwebserver.MockWebServer;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.DisplayName;
28+
import org.junit.jupiter.params.ParameterizedTest;
29+
import org.junit.jupiter.params.provider.ValueSource;
30+
31+
import java.util.Arrays;
32+
import java.util.Collections;
33+
import java.util.concurrent.TimeUnit;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
class ConnectionPoolLeakageTest {
38+
39+
private MockWebServer server;
40+
41+
private ConnectionPool connectionPool;
42+
private OkHttpClientBuilderImpl clientBuilder;
43+
44+
@BeforeEach
45+
void setUp() {
46+
server = new MockWebServer();
47+
final char[] chars = new char[10485760];
48+
Arrays.fill(chars, '1');
49+
server.enqueue(new MockResponse().setResponseCode(200).setChunkedBody(new String(chars), 1024));
50+
connectionPool = new ConnectionPool(10, 100, TimeUnit.SECONDS);
51+
clientBuilder = new OkHttpClientFactory().newBuilder();
52+
clientBuilder.getBuilder().connectionPool(connectionPool);
53+
}
54+
55+
@AfterEach
56+
void tearDown() throws Exception {
57+
server.shutdown();
58+
connectionPool.evictAll();
59+
}
60+
61+
@ParameterizedTest(name = "with protocol {0}")
62+
@DisplayName("consumeBytes should not leak connections")
63+
@ValueSource(strings = { "h2_prior_knowledge", "http/1.1" })
64+
void consumeBytes(String protocol) throws Exception {
65+
final Protocol p = Protocol.get(protocol);
66+
server.setProtocols(Collections.singletonList(p));
67+
server.start();
68+
clientBuilder.getBuilder().protocols(Collections.singletonList(p));
69+
try (HttpClient httpClient = clientBuilder.build()) {
70+
final HttpResponse<AsyncBody> asyncBodyResponse = httpClient.consumeBytes(
71+
httpClient.newHttpRequestBuilder().uri(server.url("/").toString()).build(),
72+
(value, asyncBody) -> {
73+
asyncBody.consume();
74+
})
75+
.get(10L, TimeUnit.SECONDS);
76+
assertThat(activeConnections()).isEqualTo(1);
77+
asyncBodyResponse.body().consume();
78+
asyncBodyResponse.body().done().get(10L, TimeUnit.SECONDS);
79+
assertThat(activeConnections()).isZero();
80+
}
81+
}
82+
83+
private int activeConnections() {
84+
return connectionPool.connectionCount() - connectionPool.idleConnectionCount();
85+
}
86+
}

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

-24
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@
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;
2820

2921
@SuppressWarnings("java:S2187")
3022
public class OkHttpAsyncBodyTest extends AbstractAsyncBodyTest {
@@ -33,20 +25,4 @@ protected HttpClient.Factory getHttpClientFactory() {
3325
return new OkHttpClientFactory();
3426
}
3527

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-
}
5228
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.okhttp;
17+
18+
import okio.BufferedSource;
19+
import org.junit.jupiter.api.Test;
20+
import org.mockito.Mockito;
21+
22+
import java.nio.ByteBuffer;
23+
import java.util.List;
24+
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.when;
27+
28+
class OkHttpImplAsyncBodyTest {
29+
30+
@Test
31+
void testClosedWhenExhausted() throws Exception {
32+
BufferedSource source = mock(BufferedSource.class);
33+
when(source.exhausted()).thenReturn(true);
34+
OkHttpClientImpl.OkHttpAsyncBody<List<ByteBuffer>> asyncBody = new OkHttpClientImpl.OkHttpAsyncBody<List<ByteBuffer>>(null,
35+
source,
36+
Runnable::run) {
37+
38+
@Override
39+
protected List<ByteBuffer> process(BufferedSource source) {
40+
return null;
41+
}
42+
};
43+
44+
asyncBody.consume();
45+
Mockito.verify(source).close();
46+
}
47+
}

0 commit comments

Comments
 (0)