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-servlets-get.git
The following commit(s) were added to refs/heads/master by this push:
new ff87b8a SLING-13354 - StreamRenderer: set text/html when rendering an
extensionless index file (#26)
ff87b8a is described below
commit ff87b8a29f8da2e8f97e5e1de3242423faf78859
Author: David Sanchez Monedero
<[email protected]>
AuthorDate: Thu Nov 12 10:43:42 2026 +0100
SLING-13354 - StreamRenderer: set text/html when rendering an extensionless
index file (#26)
* SLING-13354 - StreamRenderer: set content type to text/html when
rendering an extensionless index file
---
.../servlets/get/impl/helpers/StreamRenderer.java | 22 +++--
.../get/impl/helpers/StreamRendererTest.java | 99 ++++++++++++++++++++++
2 files changed, 116 insertions(+), 5 deletions(-)
diff --git
a/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRenderer.java
b/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRenderer.java
index d495df9..05c7764 100644
---
a/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRenderer.java
+++
b/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRenderer.java
@@ -78,6 +78,10 @@ public class StreamRenderer implements Renderer {
// Accept-Ranges header value
private static final String ACCEPT_RANGES_BYTES = "bytes";
+ private static final String CONTENT_TYPE_APPLICATION_OCTET_STREAM =
"application/octet-stream";
+
+ private static final String CONTENT_TYPE_TEXT_HTML = "text/html";
+
/**
* Full range marker.
*/
@@ -301,6 +305,15 @@ public class StreamRenderer implements Renderer {
setHeaders(fileRes, response);
+ final boolean noExtension = index.indexOf('.') < 0;
+
+ // An index resource without extension is rendered through
"<path>.html", so the
+ // response is always HTML, no matter which content type
setHeaders() determined for
+ // the resource itself. Do it before the HEAD check so HEAD
and GET report the same type.
+ if (noExtension) {
+ response.setContentType(CONTENT_TYPE_TEXT_HTML);
+ }
+
if (isHeadRequest(request)) {
return;
}
@@ -311,9 +324,8 @@ public class StreamRenderer implements Renderer {
rdo.setReplaceSelectors("");
RequestDispatcher dispatcher;
- if (index.indexOf('.') < 0) {
- String filePath = fileRes.getPath() + ".html";
- dispatcher = request.getRequestDispatcher(filePath, rdo);
+ if (noExtension) {
+ dispatcher =
request.getRequestDispatcher(fileRes.getPath() + ".html", rdo);
} else {
dispatcher = request.getRequestDispatcher(fileRes, rdo);
}
@@ -350,7 +362,7 @@ public class StreamRenderer implements Renderer {
response.setDateHeader(HEADER_LAST_MODIFIED, modifTime);
}
- final String defaultContentType = "application/octet-stream";
+ final String defaultContentType =
CONTENT_TYPE_APPLICATION_OCTET_STREAM;
String contentType = meta.getContentType();
if (contentType == null || defaultContentType.equals(contentType)) {
// if repository doesn't provide a content-type, or
@@ -403,7 +415,7 @@ public class StreamRenderer implements Renderer {
private void renderIndex(Resource resource,
SlingJakartaHttpServletResponse response) throws IOException {
- response.setContentType("text/html");
+ response.setContentType(CONTENT_TYPE_TEXT_HTML);
response.setCharacterEncoding("UTF-8");
String path = resource.getPath();
diff --git
a/src/test/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererTest.java
b/src/test/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererTest.java
index bf1f3cc..971e7b2 100644
---
a/src/test/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererTest.java
+++
b/src/test/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererTest.java
@@ -45,6 +45,7 @@ import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
public class StreamRendererTest {
@@ -212,4 +213,102 @@ public class StreamRendererTest {
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Mockito.verify(dispatcher).include(Mockito.any(), Mockito.any());
}
+
+ @Test
+ public void
test_render_directory_indexWithoutExtension_setsHtmlContentType() throws
IOException, ServletException {
+ final ServletContext sc = Mockito.mock(ServletContext.class);
+ final RequestDispatcher dispatcher =
Mockito.mock(RequestDispatcher.class);
+ final SlingJakartaHttpServletRequest internalRequest =
Mockito.mock(SlingJakartaHttpServletRequest.class);
+ Mockito.when(internalRequest.getRequestDispatcher(
+ Mockito.anyString(),
Mockito.any(RequestDispatcherOptions.class)))
+ .thenReturn(dispatcher);
+
+ Resource root = context.resourceResolver().getResource("/");
+ final SlingJakartaHttpServletRequest request =
Builders.newRequestBuilder(root)
+ .useRequestDispatcherFrom(internalRequest)
+ .buildJakartaRequest();
+ final SlingJakartaHttpServletResponseResult response =
+ Builders.newResponseBuilder().buildJakartaResponseResult();
+
+ // index name without '.' makes the renderer include "<path>.html"
+ StreamRenderer renderer = new StreamRenderer(true, new String[] {"/"},
sc);
+ renderer.render(request, response);
+
+ assertEquals("text/html", response.getContentType());
+ Mockito.verify(dispatcher).include(Mockito.any(), Mockito.any());
+ }
+
+ @Test
+ public void
test_render_directory_head_indexWithoutExtension_setsHtmlContentType() throws
IOException {
+ final ServletContext sc = Mockito.mock(ServletContext.class);
+ final RequestDispatcher dispatcher =
Mockito.mock(RequestDispatcher.class);
+ final SlingJakartaHttpServletRequest internalRequest =
Mockito.mock(SlingJakartaHttpServletRequest.class);
+
+ Resource root = context.resourceResolver().getResource("/");
+ final SlingJakartaHttpServletRequest request =
Builders.newRequestBuilder(root)
+ .withRequestMethod("HEAD")
+ .useRequestDispatcherFrom(internalRequest)
+ .buildJakartaRequest();
+ final SlingJakartaHttpServletResponseResult response =
+ Builders.newResponseBuilder().buildJakartaResponseResult();
+
+ StreamRenderer renderer = new StreamRenderer(true, new String[] {"/"},
sc);
+ renderer.render(request, response);
+
+ // HEAD must announce the same content type as GET and must not
include anything
+ assertEquals("text/html", response.getContentType());
+ Mockito.verifyNoInteractions(dispatcher);
+ }
+
+ @Test
+ public void test_render_directory_indexWithExtension_keepsContentType()
throws IOException, ServletException {
+ final ServletContext sc = Mockito.mock(ServletContext.class);
+ final RequestDispatcher dispatcher =
Mockito.mock(RequestDispatcher.class);
+ final SlingJakartaHttpServletRequest internalRequest =
Mockito.mock(SlingJakartaHttpServletRequest.class);
+ Mockito.when(internalRequest.getRequestDispatcher(
+ Mockito.any(Resource.class),
Mockito.any(RequestDispatcherOptions.class)))
+ .thenReturn(dispatcher);
+
+ Resource root = context.resourceResolver().getResource("/");
+ final SlingJakartaHttpServletRequest request =
Builders.newRequestBuilder(root)
+ .useRequestDispatcherFrom(internalRequest)
+ .buildJakartaRequest();
+ final SlingJakartaHttpServletResponseResult response =
+ Builders.newResponseBuilder().buildJakartaResponseResult();
+
+ // index name with '.' is included as is, the content type must not be
forced to text/html
+ StreamRenderer renderer = new StreamRenderer(true, new String[]
{"file.txt"}, sc);
+ renderer.render(request, response);
+
+ assertNotEquals("text/html", response.getContentType());
+ Mockito.verify(dispatcher).include(Mockito.any(), Mockito.any());
+ }
+
+ @Test
+ public void
test_render_directory_indexWithoutExtension_overridesExistingContentType()
+ throws IOException, ServletException {
+ // an extensionless index that already has its own content type
+ context.build().file("index", new
ByteArrayInputStream("x".getBytes()), "text/plain", 0L);
+
+ final ServletContext sc = Mockito.mock(ServletContext.class);
+ final RequestDispatcher dispatcher =
Mockito.mock(RequestDispatcher.class);
+ final SlingJakartaHttpServletRequest internalRequest =
Mockito.mock(SlingJakartaHttpServletRequest.class);
+ Mockito.when(internalRequest.getRequestDispatcher(
+ Mockito.anyString(),
Mockito.any(RequestDispatcherOptions.class)))
+ .thenReturn(dispatcher);
+
+ Resource root = context.resourceResolver().getResource("/");
+ final SlingJakartaHttpServletRequest request =
Builders.newRequestBuilder(root)
+ .useRequestDispatcherFrom(internalRequest)
+ .buildJakartaRequest();
+ final SlingJakartaHttpServletResponseResult response =
+ Builders.newResponseBuilder().buildJakartaResponseResult();
+
+ // "/index" has its own content type, but it is rendered via
"/index.html"
+ StreamRenderer renderer = new StreamRenderer(true, new String[]
{"index"}, sc);
+ renderer.render(request, response);
+
+ assertEquals("text/html", response.getContentType());
+ Mockito.verify(dispatcher).include(Mockito.any(), Mockito.any());
+ }
}