diff --git a/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java b/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java index 41d71190360..4ace36efe4b 100644 --- a/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java +++ b/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java @@ -68,12 +68,15 @@ import java.net.HttpCookie; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; @@ -156,6 +159,9 @@ public class HttpMessage implements Message { */ private boolean responseFromTargetHost = false; + private static final Set WARNED_CONTENT_TYPE_VALUES = + Collections.synchronizedSet(new HashSet<>()); + public HistoryReference getHistoryRef() { return historyRef; } @@ -508,13 +514,25 @@ private static void setBodyCharset(HttpBody body, HttpHeader header) { if (charset != null && !charset.equalsIgnoreCase(body.getCharset()) && !isCharsetSupported(charset)) { - LOGGER.warn( - "Failed to set charset {} from content-type value: {}", - charset, - header.getNormalisedContentTypeValue()); + String contentTypeValue = header.getNormalisedContentTypeValue(); + if (WARNED_CONTENT_TYPE_VALUES.add(contentTypeValue)) { + LOGGER.warn( + "Failed to set charset {} from content-type value: {}", + charset, + header.getNormalisedContentTypeValue()); + } } } + /** + * Resets the set of warned content-type values. + * + *

Note: Not part of the public API. + */ + public static void resetWarnedContentTypeValues() { + WARNED_CONTENT_TYPE_VALUES.clear(); + } + private static boolean isCharsetSupported(String charset) { try { return Charset.isSupported(charset); diff --git a/zap/src/test/java/org/parosproxy/paros/network/HttpMessageUnitTest.java b/zap/src/test/java/org/parosproxy/paros/network/HttpMessageUnitTest.java index 151cf27e150..36dc3071fb4 100644 --- a/zap/src/test/java/org/parosproxy/paros/network/HttpMessageUnitTest.java +++ b/zap/src/test/java/org/parosproxy/paros/network/HttpMessageUnitTest.java @@ -21,6 +21,7 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -72,6 +73,7 @@ class HttpMessageUnitTest { @AfterEach void cleanUp() throws Exception { HttpMessage.setContentEncodingsHandler(null); + HttpMessage.resetWarnedContentTypeValues(); Configurator.reconfigure(getClass().getResource("/log4j2-test.properties").toURI()); } @@ -624,6 +626,61 @@ void shouldUseDefaultAndWarnOnUnknownCharsetWhenSettingRequestBody(String charse containsString("Failed to set charset")); } + @Test + void shouldWarnOnceOnSameUnknownCharsetWhenSettingRequestBody() throws Exception { + // Given + HttpMessage message = new HttpMessage(); + message.setRequestHeader( + new HttpRequestHeader( + "GET / HTTP/1.1\r\nContent-Type: text/plain; charset=1st_unknown")); + withLoggerAppender(); + // When + message.setRequestBody("Body"); + message.setRequestBody("Body"); + message.setRequestHeader( + new HttpRequestHeader( + "GET / HTTP/1.1\r\nContent-Type: text/plain; charset=2nd_unknown")); + message.setRequestBody("Body"); + message.setRequestBody("Body"); + // Then + assertThat(testAppender.getLogEvents(), hasSize(2)); + assertThat( + testAppender.getLogEvents().get(0).getMessage(), + allOf( + containsString("Failed to set charset"), + containsString("charset=1st_unknown"))); + assertThat( + testAppender.getLogEvents().get(1).getMessage(), + allOf( + containsString("Failed to set charset"), + containsString("charset=2nd_unknown"))); + } + + @Test + void shouldWarnOnceAgainOnSameUnknownCharsetWhenSettingRequestBodyAfterResettingWarns() + throws Exception { + // Given + HttpMessage message = new HttpMessage(); + message.setRequestHeader( + new HttpRequestHeader( + "GET / HTTP/1.1\r\nContent-Type: text/plain; charset=unknown")); + withLoggerAppender(); + // When + message.setRequestBody("Body"); + message.setRequestBody("Body"); + HttpMessage.resetWarnedContentTypeValues(); + message.setRequestBody("Body"); + message.setRequestBody("Body"); + // Then + assertThat(testAppender.getLogEvents(), hasSize(2)); + assertThat( + testAppender.getLogEvents().get(0).getMessage(), + allOf(containsString("Failed to set charset"), containsString("charset=unknown"))); + assertThat( + testAppender.getLogEvents().get(1).getMessage(), + allOf(containsString("Failed to set charset"), containsString("charset=unknown"))); + } + static Stream setBodyData() { String iso8851 = StandardCharsets.ISO_8859_1.name(); String utf8 = StandardCharsets.UTF_8.name(); @@ -681,6 +738,61 @@ void shouldUseDefaultAndWarnOnUnknownCharsetWhenSettingResponseBody(String chars containsString("Failed to set charset")); } + @Test + void shouldWarnOnceOnSameUnknownCharsetWhenSettingResponseBody() throws Exception { + // Given + HttpMessage message = new HttpMessage(); + message.setResponseHeader( + new HttpResponseHeader( + "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=1st_unknown")); + withLoggerAppender(); + // When + message.setResponseBody("Body"); + message.setResponseBody("Body"); + message.setResponseHeader( + new HttpResponseHeader( + "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=2nd_unknown")); + message.setResponseBody("Body"); + message.setResponseBody("Body"); + // Then + assertThat(testAppender.getLogEvents(), hasSize(2)); + assertThat( + testAppender.getLogEvents().get(0).getMessage(), + allOf( + containsString("Failed to set charset"), + containsString("charset=1st_unknown"))); + assertThat( + testAppender.getLogEvents().get(1).getMessage(), + allOf( + containsString("Failed to set charset"), + containsString("charset=2nd_unknown"))); + } + + @Test + void shouldWarnOnceAgainOnSameUnknownCharsetWhenSettingResponseBodyAfterResettingWarns() + throws Exception { + // Given + HttpMessage message = new HttpMessage(); + message.setResponseHeader( + new HttpResponseHeader( + "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=unknown")); + withLoggerAppender(); + // When + message.setResponseBody("Body"); + message.setResponseBody("Body"); + HttpMessage.resetWarnedContentTypeValues(); + message.setResponseBody("Body"); + message.setResponseBody("Body"); + // Then + assertThat(testAppender.getLogEvents(), hasSize(2)); + assertThat( + testAppender.getLogEvents().get(0).getMessage(), + allOf(containsString("Failed to set charset"), containsString("charset=unknown"))); + assertThat( + testAppender.getLogEvents().get(1).getMessage(), + allOf(containsString("Failed to set charset"), containsString("charset=unknown"))); + } + private static HttpMessage newHttpMessage() throws Exception { HttpMessage message = new HttpMessage(