This is an automated email from the ASF dual-hosted git repository.
joerghoh pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-engine.git
The following commit(s) were added to refs/heads/master by this push:
new 5365d44 SLING-13357 add header-modification validation for
SetCharacterEncoding (#88)
5365d44 is described below
commit 5365d440765cda7b29af99f36b48efd88ed846d3
Author: Jörg Hoh <[email protected]>
AuthorDate: Thu Sep 24 11:53:17 2026 +0200
SLING-13357 add header-modification validation for SetCharacterEncoding
(#88)
---
.../impl/SlingJakartaHttpServletResponseImpl.java | 170 ++++++-
.../impl/SlingHttpServletResponseImplTest.java | 525 ++++++++++++++++++---
2 files changed, 619 insertions(+), 76 deletions(-)
diff --git
a/src/main/java/org/apache/sling/engine/impl/SlingJakartaHttpServletResponseImpl.java
b/src/main/java/org/apache/sling/engine/impl/SlingJakartaHttpServletResponseImpl.java
index 6a5099b..a1f27f8 100644
---
a/src/main/java/org/apache/sling/engine/impl/SlingJakartaHttpServletResponseImpl.java
+++
b/src/main/java/org/apache/sling/engine/impl/SlingJakartaHttpServletResponseImpl.java
@@ -20,15 +20,18 @@ package org.apache.sling.engine.impl;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -424,7 +427,7 @@ public class SlingJakartaHttpServletResponseImpl extends
HttpServletResponseWrap
return;
}
LOG.warn(message.get());
- LOG.warn(CALL_STACK_MESSAGE + getCurrentStackTrace());
+ LOG.warn("{}{}", CALL_STACK_MESSAGE, getCurrentStackTrace());
requestData.getRequestProgressTracker().log("WARN: " +
message.get());
super.setContentType(type);
} else {
@@ -433,9 +436,81 @@ public class SlingJakartaHttpServletResponseImpl extends
HttpServletResponseWrap
}
}
+ @Override
+ public void setCharacterEncoding(final String charset) {
+ boolean isCommitedDueToSendErrorOrRedirect = this.isCommitted()
+ && (CommitReason.SEND_ERROR == this.committedReason
+ || CommitReason.SEND_REDIRECT == this.committedReason);
+ if (isCommitedDueToSendErrorOrRedirect || !isInclude()) {
+ super.setCharacterEncoding(charset);
+ return;
+ }
+ final Optional<String> message =
checkCharacterEncodingOverride(charset);
+ if (message.isPresent()) {
+ if (isCheckContentTypeOnInclude()) {
+ requestData.getRequestProgressTracker().log("ERROR: " +
message.get());
+ LOG.error("{}{}", CALL_STACK_MESSAGE, getCurrentStackTrace());
+ throw new ContentTypeChangeException(message.get());
+ }
+ if (isProtectHeadersOnInclude()) {
+ LOG.error(message.get());
+ LOG.error("{}{}", CALL_STACK_MESSAGE, getCurrentStackTrace());
+ requestData.getRequestProgressTracker().log("ERROR: " +
message.get());
+ return;
+ }
+ LOG.warn(message.get());
+ LOG.warn("{}{}", CALL_STACK_MESSAGE, getCurrentStackTrace());
+ requestData.getRequestProgressTracker().log("WARN: " +
message.get());
+ super.setCharacterEncoding(charset);
+ } else {
+ super.setCharacterEncoding(charset);
+ }
+ }
+
+ @Override
+ public void setCharacterEncoding(final Charset charset) {
+ // funnel the Charset variant through the checked String variant so
+ // that the include protections cannot be bypassed via this method
+ this.setCharacterEncoding(charset == null ? null : charset.name());
+ }
+
+ /**
+ * Checks if the response character encoding is being changed by an include
+ * and provides a message to log if it is. Changing the character encoding
+ * changes the charset parameter of the 'Content-Type' header and is
+ * therefore subject to the same include protections as
+ * {@link #setContentType(String)}.
+ *
+ * @param charset the character encoding that is being set
+ * @return an optional message to log
+ */
+ protected Optional<String> checkCharacterEncodingOverride(@Nullable String
charset) {
+ // A previously detected violation must not disable the check itself -
+ // otherwise the second and any later override attempt within the same
+ // request would pass unchecked even though the first one was blocked.
+ final boolean isFirstViolation =
+
requestData.getSlingRequestProcessor().getContentTypeHeaderState() !=
ContentTypeHeaderState.VIOLATED;
+ final String currentCharset = getCharacterEncoding();
+ if (charset != null && charsetsEqual(charset, currentCharset)) {
+ // not an effective change
+ return Optional.empty();
+ }
+
requestData.getSlingRequestProcessor().setContentTypeHeaderState(ContentTypeHeaderState.VIOLATED);
+ final String currentContentType = getContentType();
+ final String base = currentContentType == null ? "" :
getMimeTypePart(currentContentType);
+ final String newContentType = (charset == null) ? base : base +
";charset=" + charset;
+ return Optional.of(
+ isFirstViolation
+ ? getMessage(currentContentType, newContentType)
+ : getShortMessage(currentContentType, newContentType));
+ }
+
/**
* Checks if the 'Content-Type' header is being overridden and provides a
- * message to log if it is.
+ * message to log if it is. Both the media type and the charset parameter
+ * are compared: changing only the charset (e.g. from
+ * 'text/html;charset=UTF-8' to 'text/html;charset=UTF-7') changes the
+ * effective response header just as much as changing the media type.
*
* @param contentType the 'Content-Type' value that is being set
* @return an optional message to log
@@ -455,14 +530,23 @@ public class SlingJakartaHttpServletResponseImpl extends
HttpServletResponseWrap
isFirstViolation
? getMessage(currentContentType, null)
: getShortMessage(currentContentType, null));
- } else {
- Optional<String> currentMime = currentContentType == null
- ? Optional.of("null")
- : Arrays.stream(currentContentType.split(";")).findFirst();
- Optional<String> setMime =
Arrays.stream(contentType.split(";")).findFirst();
- if (currentMime.isPresent()
- && setMime.isPresent()
- && !currentMime.get().equals(setMime.get())) {
+ }
+ final String currentMime = currentContentType == null ? "null" :
getMimeTypePart(currentContentType);
+ final String setMime = getMimeTypePart(contentType);
+ if (!currentMime.equalsIgnoreCase(setMime)) {
+
requestData.getSlingRequestProcessor().setContentTypeHeaderState(ContentTypeHeaderState.VIOLATED);
+ return Optional.of(
+ isFirstViolation
+ ? getMessage(currentContentType, contentType)
+ : getShortMessage(currentContentType,
contentType));
+ }
+ final String setCharset = getCharsetPart(contentType);
+ if (setCharset != null) {
+ String currentCharset = currentContentType == null ? null :
getCharsetPart(currentContentType);
+ if (currentCharset == null) {
+ currentCharset = getCharacterEncoding();
+ }
+ if (!charsetsEqual(setCharset, currentCharset)) {
requestData.getSlingRequestProcessor().setContentTypeHeaderState(ContentTypeHeaderState.VIOLATED);
return Optional.of(
isFirstViolation
@@ -473,6 +557,70 @@ public class SlingJakartaHttpServletResponseImpl extends
HttpServletResponseWrap
return Optional.empty();
}
+ /**
+ * Returns the media type of a 'Content-Type' header value, i.e. the part
+ * before the first parameter separator, trimmed.
+ */
+ private static String getMimeTypePart(final String contentType) {
+ final int semi = contentType.indexOf(';');
+ final String mime = semi >= 0 ? contentType.substring(0, semi) :
contentType;
+ return mime.trim();
+ }
+
+ /**
+ * Returns the value of the charset parameter of a 'Content-Type' header
+ * value, or {@code null} if no charset parameter is present.
+ */
+ @Nullable
+ private static String getCharsetPart(final String contentType) {
+ final String[] parts = contentType.split(";");
+ for (int i = 1; i < parts.length; i++) {
+ final String param = parts[i].trim();
+ final int eq = param.indexOf('=');
+ if (eq > 0 && "charset".equalsIgnoreCase(param.substring(0,
eq).trim())) {
+ String value = param.substring(eq + 1).trim();
+ if (value.length() > 1 && value.charAt(0) == '"' &&
value.charAt(value.length() - 1) == '"') {
+ value = value.substring(1, value.length() - 1).trim();
+ }
+ return value.isEmpty() ? null : value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Compares two charset names for equality, treating charsets that are
+ * merely spelled differently (e.g. {@code UTF8} vs. {@code UTF-8}, or
+ * {@code Cp1252} vs. {@code windows-1252}) as equal, not just names that
+ * differ by case. Both names are resolved via {@link
Charset#forName(String)}
+ * and compared as canonical {@link Charset} instances; if either name is
+ * not a valid or supported charset name - which the Servlet API does not
+ * strictly forbid - this falls back to a case-insensitive textual
+ * comparison instead of throwing.
+ *
+ * @param a the first charset name, may be {@code null}
+ * @param b the second charset name, may be {@code null}
+ * @return {@code true} if the two names identify the same charset, or are
+ * textually equal (ignoring case) when at least one of them
+ * cannot be resolved to a {@link Charset}
+ */
+ static boolean charsetsEqual(@Nullable final String a, @Nullable final
String b) {
+ if (a == null || b == null) {
+ return Objects.equals(a, b);
+ }
+ if (a.equalsIgnoreCase(b)) {
+ return true;
+ }
+ try {
+ return Charset.forName(a).equals(Charset.forName(b));
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
+ // already known to differ textually (checked above), and at
+ // least one name is not a valid/known charset name, so canonical
+ // comparison is not possible
+ return false;
+ }
+ }
+
/**
* Short variant of {@link #getMessage(String, String)} used for repeated
* violations within the same request: it omits the include stack and the
diff --git
a/src/test/java/org/apache/sling/engine/impl/SlingHttpServletResponseImplTest.java
b/src/test/java/org/apache/sling/engine/impl/SlingHttpServletResponseImplTest.java
index 8a339c8..2476fff 100644
---
a/src/test/java/org/apache/sling/engine/impl/SlingHttpServletResponseImplTest.java
+++
b/src/test/java/org/apache/sling/engine/impl/SlingHttpServletResponseImplTest.java
@@ -19,6 +19,8 @@
package org.apache.sling.engine.impl;
import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
@@ -33,16 +35,22 @@ import org.apache.sling.engine.impl.request.DispatchingInfo;
import org.apache.sling.engine.impl.request.RequestData;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atMostOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.startsWith;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class SlingHttpServletResponseImplTest {
@@ -123,8 +131,8 @@ public class SlingHttpServletResponseImplTest {
@Test
public void testNoViolationChecksOnCommittedResponseWhenSendRedirect()
throws IOException {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
- Mockito.when(orig.isCommitted()).thenReturn(true);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ when(orig.isCommitted()).thenReturn(true);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
@@ -132,18 +140,18 @@ public class SlingHttpServletResponseImplTest {
when(requestData.getRequestProgressTracker()).thenReturn(mock(RequestProgressTracker.class));
final SlingJakartaHttpServletResponseImpl include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
- SlingJakartaHttpServletResponseImpl spyInclude = Mockito.spy(include);
+ SlingJakartaHttpServletResponseImpl spyInclude = spy(include);
spyInclude.sendRedirect("somewhere");
spyInclude.setContentType("someOtherType");
- Mockito.verify(orig, times(1)).setContentType(Mockito.any());
- Mockito.verify(spyInclude,
never()).checkContentTypeOverride(Mockito.any());
+ verify(orig, times(1)).setContentType(any());
+ verify(spyInclude, never()).checkContentTypeOverride(any());
}
@Test
public void testSendRedirectOverloadsProtectedOnInclude() throws
IOException {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -157,12 +165,12 @@ public class SlingHttpServletResponseImplTest {
include.sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY, false);
include.setTrailerFields(java.util.Collections::emptyMap);
- Mockito.verifyNoInteractions(orig);
+ verifyNoInteractions(orig);
}
@Test
public void testSendRedirectOverloadsDelegateWhenNotProtected() throws
IOException {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -174,14 +182,14 @@ public class SlingHttpServletResponseImplTest {
include.sendRedirect("/target", true);
include.sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY, false);
- Mockito.verify(orig, times(1)).sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY);
- Mockito.verify(orig, times(1)).sendRedirect("/target", true);
- Mockito.verify(orig, times(1)).sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY, false);
+ verify(orig, times(1)).sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY);
+ verify(orig, times(1)).sendRedirect("/target", true);
+ verify(orig, times(1)).sendRedirect("/target",
HttpServletResponse.SC_MOVED_PERMANENTLY, false);
}
@Test
public void testNoViolationChecksOnCommittedResponseWhenSendError() throws
IOException {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
@@ -190,21 +198,21 @@ public class SlingHttpServletResponseImplTest {
when(requestData.getRequestProgressTracker()).thenReturn(mock(RequestProgressTracker.class));
final SlingJakartaHttpServletResponseImpl include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
- SlingJakartaHttpServletResponseImpl spyInclude = Mockito.spy(include);
+ SlingJakartaHttpServletResponseImpl spyInclude = spy(include);
spyInclude.sendError(501);
// send error will eventually commit the response, let's mock this
- Mockito.when(orig.isCommitted()).thenReturn(true);
+ when(orig.isCommitted()).thenReturn(true);
spyInclude.setContentType("someOtherType");
- Mockito.verify(orig, times(1)).setContentType(Mockito.any());
- Mockito.verify(spyInclude,
never()).checkContentTypeOverride(Mockito.any());
+ verify(orig, times(1)).setContentType(any());
+ verify(spyInclude, never()).checkContentTypeOverride(any());
}
@Test
public void testViolationChecksOnCommittedResponses() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
- Mockito.when(orig.isCommitted()).thenReturn(true);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ when(orig.isCommitted()).thenReturn(true);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
@@ -215,11 +223,11 @@ public class SlingHttpServletResponseImplTest {
when(requestData.getRequestProgressTracker()).thenReturn(rpt);
final SlingJakartaHttpServletResponseImpl include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
- SlingJakartaHttpServletResponseImpl spyInclude = Mockito.spy(include);
+ SlingJakartaHttpServletResponseImpl spyInclude = spy(include);
spyInclude.setContentType("someOtherType");
- Mockito.verify(orig, times(1)).setContentType(Mockito.any());
- Mockito.verify(spyInclude,
Mockito.times(1)).checkContentTypeOverride(Mockito.any());
+ verify(orig, times(1)).setContentType(any());
+ verify(spyInclude, times(1)).checkContentTypeOverride(any());
}
@Test
@@ -236,13 +244,13 @@ public class SlingHttpServletResponseImplTest {
when(originalResponse.isCommitted()).thenReturn(false);
includeResponse.reset();
verify(originalResponse, times(1)).isCommitted();
- Mockito.verifyNoMoreInteractions(originalResponse);
+ verifyNoMoreInteractions(originalResponse);
when(originalResponse.isCommitted()).thenReturn(true);
includeResponse.reset();
verify(originalResponse, times(2)).isCommitted();
verify(originalResponse, times(1)).reset();
- Mockito.verifyNoMoreInteractions(originalResponse);
+ verifyNoMoreInteractions(originalResponse);
}
@Test
@@ -261,11 +269,11 @@ public class SlingHttpServletResponseImplTest {
includeResponse.reset();
verify(originalResponse, times(1)).reset();
- Mockito.verifyNoMoreInteractions(originalResponse);
+ verifyNoMoreInteractions(originalResponse);
}
private String callTesteeAndGetRequestProgressTrackerMessage(String[]
logMessages) {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -288,13 +296,13 @@ public class SlingHttpServletResponseImplTest {
include.setLocale(null);
include.setBufferSize(4500);
- Mockito.verify(orig, never()).setContentLength(54);
- Mockito.verify(orig, never()).setContentLengthLong(33L);
- Mockito.verify(orig, never()).setContentType("text/plain");
- Mockito.verify(orig, never()).setLocale(null);
- Mockito.verify(orig, Mockito.times(1)).setBufferSize(4500);
+ verify(orig, never()).setContentLength(54);
+ verify(orig, never()).setContentLengthLong(33L);
+ verify(orig, never()).setContentType("text/plain");
+ verify(orig, never()).setLocale(null);
+ verify(orig, times(1)).setBufferSize(4500);
- Mockito.verify(requestProcessor,
atMostOnce()).setContentTypeHeaderState(Mockito.any());
+ verify(requestProcessor,
atMostOnce()).setContentTypeHeaderState(any());
ArgumentCaptor<String> logCaptor =
ArgumentCaptor.forClass(String.class);
verify(requestProgressTracker, times(1)).log(logCaptor.capture());
@@ -336,7 +344,7 @@ public class SlingHttpServletResponseImplTest {
@Test
public void testContentMethodsOnForward() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.FORWARD);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -352,18 +360,18 @@ public class SlingHttpServletResponseImplTest {
include.setLocale(null);
include.setBufferSize(4500);
- Mockito.verify(orig, times(1)).setContentLength(54);
- Mockito.verify(orig, times(1)).setContentLengthLong(33L);
- Mockito.verify(orig, times(1)).setContentType("text/plain");
- Mockito.verify(orig, times(1)).setLocale(null);
- Mockito.verify(orig, times(1)).setBufferSize(4500);
+ verify(orig, times(1)).setContentLength(54);
+ verify(orig, times(1)).setContentLengthLong(33L);
+ verify(orig, times(1)).setContentType("text/plain");
+ verify(orig, times(1)).setLocale(null);
+ verify(orig, times(1)).setBufferSize(4500);
- Mockito.verifyNoInteractions(requestProgressTracker);
+ verifyNoInteractions(requestProgressTracker);
}
@Test
public void testContentTypeOverrideEnabled() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -386,7 +394,7 @@ public class SlingHttpServletResponseImplTest {
} catch (RuntimeException e) {
throwable = e;
}
- Mockito.verify(orig, never()).setContentType("application/json");
+ verify(orig, never()).setContentType("application/json");
ArgumentCaptor<String> logCaptor =
ArgumentCaptor.forClass(String.class);
verify(requestProgressTracker, times(1)).log(logCaptor.capture());
String logMessage = logCaptor.getValue();
@@ -414,7 +422,7 @@ public class SlingHttpServletResponseImplTest {
@Test
public void testContentTypeOverrideEnforcedForSetHeader() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -447,14 +455,14 @@ public class SlingHttpServletResponseImplTest {
}
assertNotNull("Expected addHeader(\"content-type\", ...) to be
blocked.", addHeaderThrowable);
- Mockito.verify(orig, never()).setHeader(Mockito.anyString(),
Mockito.anyString());
- Mockito.verify(orig, never()).addHeader(Mockito.anyString(),
Mockito.anyString());
- Mockito.verify(orig, never()).setContentType(Mockito.anyString());
+ verify(orig, never()).setHeader(anyString(), anyString());
+ verify(orig, never()).addHeader(anyString(), anyString());
+ verify(orig, never()).setContentType(anyString());
}
@Test
public void testUnrelatedHeadersNotRoutedThroughContentTypeCheck() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -466,13 +474,13 @@ public class SlingHttpServletResponseImplTest {
include.setHeader("X-Custom", "value");
include.addHeader("X-Custom", "another");
- Mockito.verify(orig, times(1)).setHeader("X-Custom", "value");
- Mockito.verify(orig, times(1)).addHeader("X-Custom", "another");
+ verify(orig, times(1)).setHeader("X-Custom", "value");
+ verify(orig, times(1)).addHeader("X-Custom", "another");
}
@Test
public void testContentTypeOverrideDisabled() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -488,7 +496,7 @@ public class SlingHttpServletResponseImplTest {
final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
include.setContentType("application/json");
- Mockito.verify(orig, times(1)).setContentType("application/json");
+ verify(orig, times(1)).setContentType("application/json");
ArgumentCaptor<String> logCaptor =
ArgumentCaptor.forClass(String.class);
verify(requestProgressTracker, times(1)).log(logCaptor.capture());
@@ -507,7 +515,7 @@ public class SlingHttpServletResponseImplTest {
@Test
public void testContentTypeOverrideStillEnforcedAfterPreviousViolation() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -531,12 +539,12 @@ public class SlingHttpServletResponseImplTest {
throwable = e;
}
assertNotNull("Expected the repeated override attempt to still be
blocked.", throwable);
- Mockito.verify(orig, never()).setContentType("text/html");
+ verify(orig, never()).setContentType("text/html");
}
@Test
public void
testContentTypeOverrideStillIgnoredAfterPreviousViolationWithProtectHeaders() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -555,12 +563,12 @@ public class SlingHttpServletResponseImplTest {
include.setContentType("text/html");
- Mockito.verify(orig, never()).setContentType("text/html");
+ verify(orig, never()).setContentType("text/html");
}
@Test
public void testNoOverrideProtectHeadersContentTypeOverride() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
@@ -576,13 +584,343 @@ public class SlingHttpServletResponseImplTest {
final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
include.setContentType("application/json");
- Mockito.verify(orig, times(1)).setContentType("application/json");
- Mockito.verifyNoInteractions(requestProgressTracker);
+ verify(orig, times(1)).setContentType("application/json");
+ verifyNoInteractions(requestProgressTracker);
+ }
+
+ @Test
+ public void testCharacterEncodingProtectedOnInclude() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ when(orig.getContentType()).thenReturn("text/html;charset=UTF-8");
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setProtectHeadersOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ include.setCharacterEncoding("ISO-2022-JP");
+
+ verify(orig, never()).setCharacterEncoding(anyString());
+ ArgumentCaptor<String> logCaptor =
ArgumentCaptor.forClass(String.class);
+ verify(requestProgressTracker, times(1)).log(logCaptor.capture());
+ assertTrue(logCaptor.getValue().startsWith("ERROR: "));
+ }
+
+ @Test
+ public void testCharacterEncodingCharsetVariantProtectedOnInclude() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setProtectHeadersOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ include.setCharacterEncoding(StandardCharsets.UTF_16);
+
+ verify(orig, never()).setCharacterEncoding(anyString());
+ verify(orig, never()).setCharacterEncoding(any(Charset.class));
+ }
+
+ @Test
+ public void testCharacterEncodingUnchangedNotFlaggedOnInclude() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+ info.setProtectHeadersOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ include.setCharacterEncoding("utf-8");
+
+ verify(orig, times(1)).setCharacterEncoding("utf-8");
+ verifyNoInteractions(requestProgressTracker);
+ }
+
+ @Test
+ public void testCharacterEncodingDelegatedOutsideInclude() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.FORWARD);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
+ final HttpServletResponse response = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ response.setCharacterEncoding("ISO-2022-JP");
+
+ verify(orig, times(1)).setCharacterEncoding("ISO-2022-JP");
+ }
+
+ @Test
+ public void testContentTypeCharsetChangeDetectedOnInclude() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+ when(orig.getContentType()).thenReturn("text/html;charset=UTF-8");
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ Throwable throwable = null;
+ try {
+ include.setContentType("text/html;charset=UTF-7");
+ } catch (RuntimeException e) {
+ throwable = e;
+ }
+ assertNotNull("Expected a RuntimeException for the charset change.",
throwable);
+ verify(orig, never()).setContentType(anyString());
+ }
+
+ @Test
+ public void testContentTypeSameMimeAndCharsetCaseInsensitiveNotFlagged() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+ when(orig.getContentType()).thenReturn("text/html; charset=UTF-8");
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+ info.setProtectHeadersOnInclude(true);
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ include.setContentType("TEXT/HTML;charset=utf-8");
+
+ verify(orig, times(1)).setContentType("TEXT/HTML;charset=utf-8");
+ verifyNoInteractions(requestProgressTracker);
+ }
+
+ @Test
+ public void testCharacterEncodingCheckContentTypeOnIncludeThrows() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ when(orig.getContentType()).thenReturn("text/html;charset=UTF-8");
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ Throwable throwable = null;
+ try {
+ include.setCharacterEncoding("ISO-2022-JP");
+ } catch (RuntimeException e) {
+ throwable = e;
+ }
+ assertNotNull("Expected a RuntimeException for the character encoding
change.", throwable);
+ verify(orig, never()).setCharacterEncoding(anyString());
+ verify(requestProgressTracker, times(1)).log(startsWith("ERROR: "));
+ }
+
+ @Test
+ public void testCharacterEncodingStillEnforcedAfterPreviousViolation() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+ when(orig.getContentType()).thenReturn("text/html;charset=UTF-8");
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+ // a violation has already been detected earlier within this request
+
when(requestProcessor.getContentTypeHeaderState()).thenReturn(ContentTypeHeaderState.VIOLATED);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ Throwable throwable = null;
+ try {
+ include.setCharacterEncoding("ISO-2022-JP");
+ } catch (RuntimeException e) {
+ throwable = e;
+ }
+ assertNotNull("Expected the repeated override attempt to still be
blocked.", throwable);
+ verify(orig, never()).setCharacterEncoding(anyString());
+ // the short message must not require the RequestProgressTracker
messages again
+ verify(requestProgressTracker, never()).getMessages();
+ }
+
+ @Test
+ public void
testCharacterEncodingNoViolationChecksOnCommittedResponseWhenSendRedirect()
throws IOException {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ when(orig.isCommitted()).thenReturn(true);
+
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(mock(RequestProgressTracker.class));
+
+ final SlingJakartaHttpServletResponseImpl include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+ SlingJakartaHttpServletResponseImpl spyInclude = spy(include);
+
+ spyInclude.sendRedirect("somewhere");
+
+ spyInclude.setCharacterEncoding("ISO-2022-JP");
+ verify(orig, times(1)).setCharacterEncoding("ISO-2022-JP");
+ verify(spyInclude, never()).checkCharacterEncodingOverride(any());
+ }
+
+ @Test
+ public void
testCharacterEncodingNoViolationChecksOnCommittedResponseWhenSendError() throws
IOException {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getSlingRequestProcessor()).thenReturn(mock(SlingRequestProcessorImpl.class));
+
when(requestData.getRequestProgressTracker()).thenReturn(mock(RequestProgressTracker.class));
+
+ final SlingJakartaHttpServletResponseImpl include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+ SlingJakartaHttpServletResponseImpl spyInclude = spy(include);
+
+ spyInclude.sendError(501);
+ // send error will eventually commit the response, let's mock this
+ when(orig.isCommitted()).thenReturn(true);
+
+ spyInclude.setCharacterEncoding("ISO-2022-JP");
+ verify(orig, times(1)).setCharacterEncoding("ISO-2022-JP");
+ verify(spyInclude, never()).checkCharacterEncodingOverride(any());
+ }
+
+ @Test
+ public void testCharacterEncodingNullCharsetFallsBackToDelegate() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ when(orig.getContentType()).thenReturn("text/html;charset=UTF-8");
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setProtectHeadersOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ include.setCharacterEncoding((Charset) null);
+
+ verify(orig, never()).setCharacterEncoding(anyString());
+ verify(requestProgressTracker, times(1)).log(startsWith("ERROR: "));
+ }
+
+ @Test
+ public void
testContentTypeCharsetFallsBackToCurrentCharacterEncodingWhenNotInContentType()
{
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+ // current 'Content-Type' has no charset parameter, but the response
+ // already has a character encoding assigned
+ when(orig.getContentType()).thenReturn("text/html");
+ when(orig.getCharacterEncoding()).thenReturn("UTF-8");
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+
when(requestData.getActiveServletName()).thenReturn(ACTIVE_SERVLET_NAME);
+ ArrayList<String> logMessagesList = new
ArrayList<>(Arrays.asList(logMessages));
+ when(requestProgressTracker.getMessages()).thenAnswer(invocation ->
logMessagesList.iterator());
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ Throwable throwable = null;
+ try {
+ include.setContentType("text/html;charset=UTF-7");
+ } catch (RuntimeException e) {
+ throwable = e;
+ }
+ assertNotNull(
+ "Expected a RuntimeException since the charset differs from
the current character encoding.",
+ throwable);
+ verify(orig, never()).setContentType(anyString());
+ }
+
+ @Test
+ public void testContentTypeQuotedCharsetParsedCorrectly() {
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
+ final RequestData requestData = mock(RequestData.class);
+ final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
+ final RequestProgressTracker requestProgressTracker =
mock(RequestProgressTracker.class);
+ when(requestData.getDispatchingInfo()).thenReturn(info);
+ when(orig.getContentType()).thenReturn("text/html; charset=\"UTF-8\"");
+
when(requestData.getRequestProgressTracker()).thenReturn(requestProgressTracker);
+ info.setProtectHeadersOnInclude(true);
+ info.setCheckContentTypeOnInclude(true);
+
+ final SlingRequestProcessorImpl requestProcessor =
mock(SlingRequestProcessorImpl.class);
+
when(requestData.getSlingRequestProcessor()).thenReturn(requestProcessor);
+
+ final HttpServletResponse include = new
SlingJakartaHttpServletResponseImpl(requestData, orig);
+
+ // same mime type and same (quoted vs. unquoted) charset must not be
flagged
+ include.setContentType("text/html; charset=UTF-8");
+
+ verify(orig, times(1)).setContentType("text/html; charset=UTF-8");
+ verifyNoInteractions(requestProgressTracker);
}
@Test
public void testCookies() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -592,12 +930,12 @@ public class SlingHttpServletResponseImplTest {
include.addCookie(new Cookie("foo", "bar"));
- Mockito.verifyNoInteractions(orig);
+ verifyNoInteractions(orig);
}
@Test
public void testSendError() throws IOException {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -608,12 +946,12 @@ public class SlingHttpServletResponseImplTest {
include.sendError(500);
include.sendError(500, "Error");
- Mockito.verifyNoInteractions(orig);
+ verifyNoInteractions(orig);
}
@Test
public void testSetStatus() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -623,12 +961,12 @@ public class SlingHttpServletResponseImplTest {
include.setStatus(500);
- Mockito.verifyNoInteractions(orig);
+ verifyNoInteractions(orig);
}
@Test
public void testHeaders() {
- final SlingJakartaHttpServletResponse orig =
Mockito.mock(SlingJakartaHttpServletResponse.class);
+ final SlingJakartaHttpServletResponse orig =
mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new
DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
@@ -643,6 +981,63 @@ public class SlingHttpServletResponseImplTest {
include.setHeader("foo", "value");
include.addHeader("bar", "another");
- Mockito.verifyNoInteractions(orig);
+ verifyNoInteractions(orig);
+ }
+
+ @Test
+ public void testCharsetsEqualIdenticalNames() {
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual("UTF-8",
"UTF-8"));
+ }
+
+ @Test
+ public void testCharsetsEqualDifferingOnlyByCase() {
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual("utf-8",
"UTF-8"));
+ }
+
+ @Test
+ public void testCharsetsEqualAliasNames() {
+ // "UTF8" and "UTF-8" differ by more than case, but are the same
+ // charset (UTF8 is a registered alias)
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual("UTF8",
"UTF-8"));
+ // "Cp1252" and "windows-1252" are aliases for the same charset
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual("Cp1252",
"windows-1252"));
+ }
+
+ @Test
+ public void testCharsetsEqualDifferentCharsets() {
+ assertFalse(SlingJakartaHttpServletResponseImpl.charsetsEqual("UTF-8",
"ISO-8859-1"));
+ }
+
+ @Test
+ public void testCharsetsEqualBothNull() {
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual(null,
null));
+ }
+
+ @Test
+ public void testCharsetsEqualOneNull() {
+ assertFalse(SlingJakartaHttpServletResponseImpl.charsetsEqual(null,
"UTF-8"));
+ assertFalse(SlingJakartaHttpServletResponseImpl.charsetsEqual("UTF-8",
null));
+ }
+
+ @Test
+ public void
testCharsetsEqualIdenticalUnsupportedNamesFallsBackToTextualMatch() {
+ // neither name resolves to a known Charset, but they are textually
+ // equal (ignoring case), so the fast path short-circuits before any
+ // Charset.forName lookup is attempted
+ assertTrue(SlingJakartaHttpServletResponseImpl.charsetsEqual(
+ "bogus-unknown-charset-xyz", "BOGUS-UNKNOWN-CHARSET-XYZ"));
+ }
+
+ @Test
+ public void testCharsetsEqualUnsupportedNameFallsBackWithoutThrowing() {
+ // "bogus-unknown-charset-xyz" is a syntactically valid charset name
+ // that is simply not registered/supported
(UnsupportedCharsetException)
+
assertFalse(SlingJakartaHttpServletResponseImpl.charsetsEqual("bogus-unknown-charset-xyz",
"UTF-8"));
+ }
+
+ @Test
+ public void testCharsetsEqualIllegalCharsetNameFallsBackWithoutThrowing() {
+ // this name is not even syntactically valid
(IllegalCharsetNameException)
+ assertFalse(SlingJakartaHttpServletResponseImpl.charsetsEqual("not a
real charset!!", "UTF-8"));
}
}