Skip to content

Commit a53ff64

Browse files
committed
feat: HttpLoggingInterceptor logs only plain text response bodies
Signed-off-by: Marc Nuri <marc@marcnuri.com>
1 parent eb46228 commit a53ff64

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/BufferUtil.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package io.fabric8.kubernetes.client.http;
1717

1818
import java.nio.ByteBuffer;
19+
import java.nio.charset.CharacterCodingException;
20+
import java.nio.charset.CharsetDecoder;
21+
import java.nio.charset.StandardCharsets;
1922
import java.util.Arrays;
2023
import java.util.Collection;
2124

@@ -70,4 +73,24 @@ public static ByteBuffer copy(ByteBuffer buffer) {
7073
buffer.position(position);
7174
return clone;
7275
}
76+
77+
/**
78+
* Very rudimentary method to check if the provided ByteBuffer contains text.
79+
*
80+
* @return true if the buffer contains text, false otherwise.
81+
*/
82+
public static boolean isPlainText(ByteBuffer originalBuffer) {
83+
if (originalBuffer == null) {
84+
return false;
85+
}
86+
final ByteBuffer buffer = copy(originalBuffer);
87+
final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
88+
try {
89+
decoder.decode(buffer);
90+
return true;
91+
} catch (CharacterCodingException ex) {
92+
return false;
93+
}
94+
}
95+
7396
}

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpLoggingInterceptor.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ public DeferredLoggingConsumer(HttpLogger httpLogger, HttpRequest originalReques
113113

114114
@Override
115115
public void consume(List<ByteBuffer> value, AsyncBody asyncBody) throws Exception {
116-
// TODO: Should try to detect if the body is text or not? (HttpLoggingInterceptor.isPlainText)
117-
value.stream().map(BufferUtil::copy).forEach(responseBody::add); // Potential leak
116+
if (!value.isEmpty() && BufferUtil.isPlainText(value.iterator().next())) {
117+
value.stream().map(BufferUtil::copy).forEach(responseBody::add); // Potential leak
118+
}
118119
originalConsumer.consume(value, asyncBody);
119120
}
120121

@@ -133,7 +134,7 @@ public void accept(Void v, Throwable throwable) {
133134

134135
/**
135136
* Registers the asyncBody.done() callback.
136-
*
137+
*
137138
* @param asyncBody the AsyncBody instance to register the callback on the done() future.
138139
*/
139140
private void processAsyncBody(AsyncBody asyncBody) {
@@ -168,7 +169,7 @@ void logResponse(HttpResponse<?> response) {
168169
}
169170

170171
void logResponseBody(Queue<ByteBuffer> responseBody) {
171-
if (logger.isTraceEnabled() && responseBody != null) {
172+
if (logger.isTraceEnabled() && responseBody != null && !responseBody.isEmpty()) {
172173
final StringBuilder bodyString = new StringBuilder();
173174
while (!responseBody.isEmpty()) {
174175
bodyString.append(StandardCharsets.UTF_8.decode(responseBody.poll()));

kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpLoggingInterceptorTest.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
*/
1616
package io.fabric8.kubernetes.client.http;
1717

18+
import io.fabric8.mockwebserver.Context;
1819
import io.fabric8.mockwebserver.DefaultMockServer;
20+
import io.fabric8.mockwebserver.ServerRequest;
21+
import io.fabric8.mockwebserver.ServerResponse;
22+
import io.fabric8.mockwebserver.internal.SimpleRequest;
1923
import io.fabric8.mockwebserver.utils.ResponseProviders;
24+
import okhttp3.mockwebserver.MockResponse;
25+
import okhttp3.mockwebserver.MockWebServer;
26+
import okhttp3.mockwebserver.RecordedRequest;
27+
import okio.Buffer;
2028
import org.junit.jupiter.api.AfterAll;
2129
import org.junit.jupiter.api.BeforeAll;
2230
import org.junit.jupiter.api.BeforeEach;
@@ -27,7 +35,11 @@
2735
import org.slf4j.Logger;
2836

2937
import java.net.URI;
38+
import java.util.ArrayDeque;
3039
import java.util.Collections;
40+
import java.util.HashMap;
41+
import java.util.Map;
42+
import java.util.Queue;
3143
import java.util.concurrent.TimeUnit;
3244

3345
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,18 +48,21 @@
3648
import static org.mockito.ArgumentMatchers.eq;
3749
import static org.mockito.Mockito.mock;
3850
import static org.mockito.Mockito.timeout;
51+
import static org.mockito.Mockito.times;
3952
import static org.mockito.Mockito.when;
4053

4154
public abstract class AbstractHttpLoggingInterceptorTest {
4255

4356
private static DefaultMockServer server;
57+
private static Map<ServerRequest, Queue<ServerResponse>> responses;
4458
private Logger logger;
4559
private InOrder inOrder;
4660
private HttpClient httpClient;
4761

4862
@BeforeAll
4963
static void beforeAll() {
50-
server = new DefaultMockServer(false);
64+
responses = new HashMap<>();
65+
server = new DefaultMockServer(new Context(), new MockWebServer(), responses, false);
5166
server.start();
5267
}
5368

@@ -157,6 +172,33 @@ public void httpResponseBodyLogged() throws Exception {
157172
inOrder.verify(logger).trace("-HTTP END-");
158173
}
159174

175+
@Test
176+
@DisplayName("HTTP binary response body is skipped")
177+
public void httpResponseBodySkipped() throws Exception {
178+
final MockResponse binaryResponse = new MockResponse()
179+
.setResponseCode(200)
180+
.setBody(new Buffer().write(new byte[] { (byte) 0xFF, (byte) 0xD8, (byte) 0x00, (byte) 0x12, (byte) 0x34 }));
181+
responses.computeIfAbsent(new SimpleRequest("/binary-response-body"), k -> new ArrayDeque<>()).add(
182+
new ServerResponse() {
183+
@Override
184+
public boolean isRepeatable() {
185+
return true;
186+
}
187+
188+
@Override
189+
public MockResponse toMockResponse(RecordedRequest recordedRequest) {
190+
return binaryResponse;
191+
}
192+
});
193+
httpClient.sendAsync(httpClient.newHttpRequestBuilder()
194+
.uri(server.url("/binary-response-body"))
195+
.build(), String.class).get(10, TimeUnit.SECONDS);
196+
inOrder.verify(logger, timeout(1000L)).trace("-HTTP START-");
197+
inOrder.verify(logger).trace(eq("< {} {}"), anyInt(), anyString());
198+
inOrder.verify(logger, times(1)).trace(anyString()); // only -HTTP END- was logged
199+
inOrder.verifyNoMoreInteractions();
200+
}
201+
160202
@Test
161203
@DisplayName("WS request URI is logged")
162204
public void wsRequestUriLogged() throws Exception {

0 commit comments

Comments
 (0)