Skip to content

Commit 6eb1f57

Browse files
authored
fix: Improve logging for API Request and API Response (#1295)
1 parent 71f55c1 commit 6eb1f57

File tree

6 files changed

+128
-32
lines changed

6 files changed

+128
-32
lines changed

src/main/java/com/box/sdk/BoxAPIRequest.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.box.sdk;
22

3+
import static com.box.sdk.BoxSensitiveDataSanitizer.sanitizeHeaders;
34
import static com.box.sdk.internal.utils.CollectionUtils.mapToString;
45
import static java.lang.String.format;
56

@@ -20,6 +21,7 @@
2021
import java.util.List;
2122
import java.util.Map;
2223
import java.util.Objects;
24+
import okhttp3.Headers;
2325
import okhttp3.MediaType;
2426
import okhttp3.Request;
2527
import okhttp3.RequestBody;
@@ -509,6 +511,10 @@ BoxFileUploadSessionPart sendForUploadPart(BoxFileUploadSession session, long of
509511
*/
510512
@Override
511513
public String toString() {
514+
return toStringWithHeaders(null);
515+
}
516+
517+
private String toStringWithHeaders(Headers headers) {
512518
String lineSeparator = System.getProperty("line.separator");
513519
StringBuilder builder = new StringBuilder();
514520
builder.append("Request");
@@ -517,6 +523,11 @@ public String toString() {
517523
builder.append(' ');
518524
builder.append(this.url.toString());
519525
builder.append(lineSeparator);
526+
if (headers != null) {
527+
builder.append("Headers:").append(lineSeparator);
528+
sanitizeHeaders(headers)
529+
.forEach(h -> builder.append(format("%s: [%s]%s", h.getFirst(), h.getSecond(), lineSeparator)));
530+
}
520531

521532
String bodyString = this.bodyToString();
522533
if (bodyString != null) {
@@ -588,6 +599,7 @@ private BoxAPIResponse trySend(ProgressListener listener) {
588599
long start = System.currentTimeMillis();
589600
Request request = composeRequest(listener);
590601
Response response;
602+
this.logRequest(request.headers());
591603
if (this.followRedirects) {
592604
response = api.execute(request);
593605
} else {
@@ -596,7 +608,6 @@ private BoxAPIResponse trySend(ProgressListener listener) {
596608
logDebug(format("[trySend] connection.connect() took %dms%n", (System.currentTimeMillis() - start)));
597609

598610
BoxAPIResponse result = BoxAPIResponse.toBoxResponse(response);
599-
this.logRequest();
600611
long getResponseStart = System.currentTimeMillis();
601612
logDebug(format(
602613
"[trySend] Get Response (read network) took %dms%n", System.currentTimeMillis() - getResponseStart
@@ -670,8 +681,8 @@ private void logDebug(String message) {
670681
}
671682
}
672683

673-
private void logRequest() {
674-
logDebug(this.toString());
684+
private void logRequest(Headers headers) {
685+
logDebug(headers != null ? this.toStringWithHeaders(headers) : this.toString());
675686
}
676687

677688
/**

src/main/java/com/box/sdk/BoxAPIResponse.java

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,34 @@ public BoxAPIResponse(
8080
this(responseCode, requestMethod, requestUrl, headers, null, null, 0);
8181
}
8282

83+
public BoxAPIResponse(int responseCode,
84+
String requestMethod,
85+
String requestUrl,
86+
Map<String, List<String>> headers,
87+
String bodyString,
88+
String contentType) {
89+
this(responseCode, requestMethod, requestUrl, headers, null, contentType, 0, bodyString);
90+
}
91+
8392
public BoxAPIResponse(int code,
8493
String requestMethod,
8594
String requestUrl,
8695
Map<String, List<String>> headers,
8796
InputStream body,
8897
String contentType,
8998
long contentLength
99+
) {
100+
this(code, requestMethod, requestUrl, headers, body, contentType, contentLength, null);
101+
}
102+
103+
public BoxAPIResponse(int code,
104+
String requestMethod,
105+
String requestUrl,
106+
Map<String, List<String>> headers,
107+
InputStream body,
108+
String contentType,
109+
long contentLength,
110+
String bodyString
90111
) {
91112
this.responseCode = code;
92113
this.requestMethod = requestMethod;
@@ -97,7 +118,10 @@ public BoxAPIResponse(int code,
97118
this.rawInputStream = body;
98119
this.contentType = contentType;
99120
this.contentLength = contentLength;
100-
storeBodyResponse(body);
121+
this.bodyString = bodyString;
122+
if (body != null) {
123+
storeBodyResponse(body);
124+
}
101125
if (isSuccess(responseCode)) {
102126
this.logResponse();
103127
} else {
@@ -106,28 +130,6 @@ public BoxAPIResponse(int code,
106130
}
107131
}
108132

109-
private void storeBodyResponse(InputStream body) {
110-
try {
111-
if (contentType != null && body != null && contentType.contains(APPLICATION_JSON) && body.available() > 0) {
112-
InputStreamReader reader = new InputStreamReader(this.getBody(), UTF_8);
113-
StringBuilder builder = new StringBuilder();
114-
char[] buffer = new char[BUFFER_SIZE];
115-
116-
int read = reader.read(buffer, 0, BUFFER_SIZE);
117-
while (read != -1) {
118-
builder.append(buffer, 0, read);
119-
read = reader.read(buffer, 0, BUFFER_SIZE);
120-
}
121-
reader.close();
122-
this.disconnect();
123-
bodyString = builder.toString();
124-
rawInputStream = new ByteArrayInputStream(bodyString.getBytes(UTF_8));
125-
}
126-
} catch (IOException e) {
127-
throw new RuntimeException("Cannot read body stream", e);
128-
}
129-
}
130-
131133
private static boolean isSuccess(int responseCode) {
132134
return responseCode >= 200 && responseCode < 400;
133135
}
@@ -196,6 +198,28 @@ private static BoxAPIResponse emptyContentResponse(Response response) {
196198
);
197199
}
198200

201+
private void storeBodyResponse(InputStream body) {
202+
try {
203+
if (contentType != null && body != null && contentType.contains(APPLICATION_JSON) && body.available() > 0) {
204+
InputStreamReader reader = new InputStreamReader(this.getBody(), UTF_8);
205+
StringBuilder builder = new StringBuilder();
206+
char[] buffer = new char[BUFFER_SIZE];
207+
208+
int read = reader.read(buffer, 0, BUFFER_SIZE);
209+
while (read != -1) {
210+
builder.append(buffer, 0, read);
211+
read = reader.read(buffer, 0, BUFFER_SIZE);
212+
}
213+
reader.close();
214+
this.disconnect();
215+
bodyString = builder.toString();
216+
rawInputStream = new ByteArrayInputStream(bodyString.getBytes(UTF_8));
217+
}
218+
} catch (IOException e) {
219+
throw new RuntimeException("Cannot read body stream", e);
220+
}
221+
}
222+
199223
/**
200224
* Gets the response code returned by the API.
201225
*
@@ -274,6 +298,8 @@ public String toString() {
274298
.append(this.requestMethod)
275299
.append(' ')
276300
.append(this.requestUrl)
301+
.append(' ')
302+
.append(this.responseCode)
277303
.append(lineSeparator)
278304
.append(contentType != null ? "Content-Type: " + contentType + lineSeparator : "")
279305
.append(headers.isEmpty() ? "" : "Headers:" + lineSeparator);
@@ -284,7 +310,10 @@ public String toString() {
284310

285311
String bodyString = this.bodyToString();
286312
if (bodyString != null && !bodyString.equals("")) {
287-
builder.append("Body:").append(lineSeparator).append(bodyString);
313+
String sanitizedBodyString = contentType.equals(APPLICATION_JSON)
314+
? BoxSensitiveDataSanitizer.sanitizeJsonBody(Json.parse(bodyString).asObject()).toString()
315+
: bodyString;
316+
builder.append("Body:").append(lineSeparator).append(sanitizedBodyString);
288317
}
289318

290319
return builder.toString().trim();
@@ -310,7 +339,7 @@ public void close() {
310339
/**
311340
* Returns a string representation of this response's body. This method is used when logging this response's body.
312341
* By default, it returns an empty string (to avoid accidentally logging binary data) unless the response contained
313-
* an error message.
342+
* an error message or content type is application/json.
314343
*
315344
* @return a string representation of this response's body.
316345
*/

src/main/java/com/box/sdk/BoxJSONResponse.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.box.sdk;
22

3+
import com.box.sdk.http.ContentType;
34
import com.eclipsesource.json.Json;
45
import com.eclipsesource.json.JsonObject;
56
import com.eclipsesource.json.ParseException;
@@ -48,7 +49,7 @@ public BoxJSONResponse(int responseCode,
4849
Map<String, List<String>> headers,
4950
JsonObject body
5051
) {
51-
super(responseCode, requestMethod, requestUrl, headers);
52+
super(responseCode, requestMethod, requestUrl, headers, body.toString(), ContentType.APPLICATION_JSON);
5253
this.jsonObject = body;
5354
}
5455

src/main/java/com/box/sdk/BoxSensitiveDataSanitizer.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.box.sdk;
22

3+
import com.eclipsesource.json.JsonObject;
34
import java.util.Arrays;
45
import java.util.HashSet;
56
import java.util.Set;
@@ -44,6 +45,26 @@ static Headers sanitizeHeaders(Headers originalHeaders) {
4445
return sanitizedHeadersBuilder.build();
4546
}
4647

48+
/**
49+
* Sanitize the json body. Only for the first level of the json.
50+
*
51+
* @param originalBody the original json body
52+
* @return the sanitized json body
53+
*/
54+
@NotNull
55+
static JsonObject sanitizeJsonBody(JsonObject originalBody) {
56+
JsonObject sanitizedBody = new JsonObject();
57+
58+
for (String key : originalBody.names()) {
59+
if (isSensitiveKey(key)) {
60+
sanitizedBody.set(key, "[REDACATED]");
61+
} else {
62+
sanitizedBody.set(key, originalBody.get(key));
63+
}
64+
}
65+
return sanitizedBody;
66+
}
67+
4768
private static boolean isSensitiveKey(@NotNull String key) {
4869
return SENSITIVE_KEYS.contains(key.toLowerCase());
4970
}

src/test/java/com/box/sdk/BoxAPIResponseTest.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import static org.hamcrest.Matchers.contains;
1010
import static org.hamcrest.Matchers.is;
1111

12+
import com.eclipsesource.json.Json;
13+
import com.eclipsesource.json.JsonObject;
1214
import java.io.ByteArrayInputStream;
1315
import java.io.IOException;
1416
import java.io.InputStream;
@@ -111,7 +113,7 @@ public void testResponseWithoutBodyToString() {
111113
BoxAPIResponse response = new BoxAPIResponse(200, "GET", "https://aaa.com", headers);
112114

113115
String str = response.toString();
114-
assertThat(str, is("Response\nGET https://aaa.com\nHeaders:\nfoo: [[bAr, zab]]"));
116+
assertThat(str, is("Response\nGET https://aaa.com 200\nHeaders:\nfoo: [[bAr, zab]]"));
115117
}
116118

117119
@Test
@@ -124,7 +126,7 @@ public void testBinaryResponseToString() {
124126
);
125127

126128
String str = response.toString();
127-
assertThat(str, is("Response\nGET https://aaa.com\nContent-Type: image/jpg\nHeaders:\nfoo: [[bAr, zab]]"));
129+
assertThat(str, is("Response\nGET https://aaa.com 202\nContent-Type: image/jpg\nHeaders:\nfoo: [[bAr, zab]]"));
128130
}
129131

130132
@Test
@@ -138,7 +140,27 @@ public void testJsonResponseToString() {
138140

139141
String str = response.toString();
140142
assertThat(str, is(
141-
"Response\nGET https://aaa.com\n"
143+
"Response\nGET https://aaa.com 202\n"
144+
+ "Content-Type: application/json\n"
145+
+ "Headers:\nfoo: [[bAr, zab]]\n"
146+
+ "Body:\n{\"foo\":\"bar\"}")
147+
);
148+
}
149+
150+
151+
@Test
152+
public void testBoxJSONResponseToString() {
153+
Map<String, List<String>> headers = new TreeMap<>();
154+
headers.put("FOO", asList("bAr", "zab"));
155+
String responseBody = "{\"foo\":\"bar\"}";
156+
JsonObject jsonBody = Json.parse(responseBody).asObject();
157+
BoxAPIResponse response = new BoxJSONResponse(
158+
202, "GET", "https://aaa.com", headers, jsonBody
159+
);
160+
161+
String str = response.toString();
162+
assertThat(str, is(
163+
"Response\nGET https://aaa.com 202\n"
142164
+ "Content-Type: application/json\n"
143165
+ "Headers:\nfoo: [[bAr, zab]]\n"
144166
+ "Body:\n{\"foo\":\"bar\"}")

src/test/java/com/box/sdk/BoxSensitiveDataSanitizerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.hamcrest.MatcherAssert.assertThat;
44
import static org.hamcrest.Matchers.is;
55

6+
import com.eclipsesource.json.JsonObject;
67
import java.util.HashMap;
78
import java.util.Map;
89
import okhttp3.Headers;
@@ -85,4 +86,15 @@ public void sanitizeAddedKeys() {
8586
assertThat(sanitizedHeaders.size(), is(1));
8687
assertThat(sanitizedHeaders.get("x-auth"), is("[REDACTED]"));
8788
}
89+
90+
@Test
91+
public void removeSensitiveDataFromJsonBody() {
92+
JsonObject body = new JsonObject()
93+
.add("authorization", "token")
94+
.add("user-agent", "java-sdk");
95+
JsonObject sanitizedBody = BoxSensitiveDataSanitizer.sanitizeJsonBody(body);
96+
97+
assertThat(sanitizedBody.get("authorization").asString(), is("[REDACATED]"));
98+
assertThat(sanitizedBody.get("user-agent").asString(), is("java-sdk"));
99+
}
88100
}

0 commit comments

Comments
 (0)