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());
+    }
 }

Reply via email to