SLING-5866 - avoid getting an InputStream for binaries in HEAD requests. Contributed by Ankit Agarwal, thanks!

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1755178 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/servlets/get/impl/DefaultGetServlet.java b/src/main/java/org/apache/sling/servlets/get/impl/DefaultGetServlet.java
index 890ab2e..b1960e0 100644
--- a/src/main/java/org/apache/sling/servlets/get/impl/DefaultGetServlet.java
+++ b/src/main/java/org/apache/sling/servlets/get/impl/DefaultGetServlet.java
@@ -42,6 +42,7 @@
 import org.apache.sling.servlets.get.impl.helpers.PlainTextRendererServlet;
 import org.apache.sling.servlets.get.impl.helpers.StreamRendererServlet;
 import org.apache.sling.servlets.get.impl.helpers.XMLRendererServlet;
+import org.apache.sling.servlets.get.impl.helpers.HeadServletResponse;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,7 +61,7 @@
     @Property(name="sling.servlet.prefix", intValue=-1, propertyPrivate=true),
     
     // Generic handler for all get requests
-    @Property(name="sling.servlet.methods", value="GET", propertyPrivate=true)
+    @Property(name="sling.servlet.methods", value={"GET", "HEAD"}, propertyPrivate=true)
 })
 public class DefaultGetServlet extends SlingSafeMethodsServlet {
 
@@ -253,6 +254,15 @@
     }
 
     @Override
+    protected void doHead(SlingHttpServletRequest request,
+                          SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        response = new HeadServletResponse(response);
+        doGet(request, response);
+    }
+
+    @Override
     public void destroy() {
 
         for (Servlet servlet : rendererMap.values()) {
diff --git a/src/main/java/org/apache/sling/servlets/get/impl/DefaultHeadServlet.java b/src/main/java/org/apache/sling/servlets/get/impl/DefaultHeadServlet.java
deleted file mode 100644
index 4808873..0000000
--- a/src/main/java/org/apache/sling/servlets/get/impl/DefaultHeadServlet.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.servlets.get.impl;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Writer;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Properties;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Service;
-import org.apache.sling.api.SlingConstants;
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.SlingHttpServletResponse;
-import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
-import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
-import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The <code>DefaultHeadServlet</code> class implements default support for the
- * HTTP <i>HEAD</i> request method. It basically wraps the response to provide
- * output which does not really write to the client and the forwards to the same
- * requested URL (resource actually) acting as if the request was placed with a
- * <i>GET</i> method.
- */
-@Component(immediate=true, policy=ConfigurationPolicy.IGNORE)
-@Service(Servlet.class)
-@Properties({
-    @Property(name="service.description", value="Default HEAD Servlet"),
-    @Property(name="service.vendor", value="The Apache Software Foundation"),
-    @Property(name="sling.servlet.resourceTypes", value="sling/servlet/default"),
-    @Property(name="sling.servlet.prefix", intValue=-1),
-    @Property(name="sling.servlet.methods", value="HEAD")    
-})
-public class DefaultHeadServlet extends SlingSafeMethodsServlet {
-
-    private static final long serialVersionUID = 7416222678552027044L;
-
-    /** default log */
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Override
-    protected void doHead(SlingHttpServletRequest request,
-            SlingHttpServletResponse response) throws ServletException,
-            IOException {
-
-        // don't do nothing if the request has already been committed
-        // or this servlet is called for a servlet include
-        if (response.isCommitted()) {
-            // committed response cannot be redirected
-            log.warn("DefaultHeadServlet: Ignoring request because response is committed");
-            request.getRequestProgressTracker().log(
-                "DefaultHeadServlet: Ignoring request because response is committed");
-            return;
-        } else if (request.getAttribute(SlingConstants.ATTR_REQUEST_SERVLET) != null) {
-            // included request will not redirect
-            log.warn("DefaultHeadServlet: Ignoring request because request is included");
-            request.getRequestProgressTracker().log(
-                "DefaultHeadServlet: Ignoring request because request is included");
-            return;
-        }
-
-        request = new HeadServletRequest(request);
-        response = new HeadServletResponse(response);
-
-        RequestDispatcher dispatcher = request.getRequestDispatcher(request.getResource());
-        if (dispatcher != null) {
-            dispatcher.forward(request, response);
-        }
-    }
-
-    /**
-     * The <code>HeadServletRequest</code> is a Sling request wrapper which
-     * simulates a GET request to the included servlets/scripts such that the
-     * HEAD request acts as if a GET request is being processed without any
-     * response data being sent back.
-     */
-    private static class HeadServletRequest extends
-            SlingHttpServletRequestWrapper {
-
-        public HeadServletRequest(SlingHttpServletRequest wrappedRequest) {
-            super(wrappedRequest);
-        }
-
-        @Override
-        public String getMethod() {
-            return "GET";
-        }
-    }
-
-    /**
-     * The <code>HeadServletResponse</code> is a Sling response wrapper which
-     * ensures that nothing will ever be written by return null writers or
-     * output streams.
-     */
-    private static class HeadServletResponse extends
-            SlingHttpServletResponseWrapper {
-
-        private ServletOutputStream stream;
-
-        private PrintWriter writer;
-
-        public HeadServletResponse(SlingHttpServletResponse wrappedResponse) {
-            super(wrappedResponse);
-        }
-
-        @Override
-        public ServletOutputStream getOutputStream() {
-            if (writer != null) {
-                throw new IllegalStateException("Writer already obtained");
-            }
-
-            if (stream == null) {
-                stream = new NullServletOutputStream();
-            }
-
-            return stream;
-        }
-
-        @Override
-        public PrintWriter getWriter() {
-            if (stream != null) {
-                throw new IllegalStateException("OutputStream already obtained");
-            }
-
-            if (writer == null) {
-                writer = new PrintWriter(new NullWriter());
-            }
-
-            return writer;
-        }
-    }
-
-    /**
-     * The <code>NullServletOutputStream</code> is a
-     * <code>ServletOutputStream</code> which simply does not write out
-     * anything.
-     *
-     * @see HeadServletResponse#getOutputStream()
-     */
-    private static class NullServletOutputStream extends ServletOutputStream {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void write(byte[] b) {
-        }
-
-        @Override
-        public void write(byte[] b, int off, int len) {
-        }
-    }
-
-    /**
-     * The <code>NullWriter</code> is a <code>Writer</code> which simply does
-     * not write out anything.
-     *
-     * @see HeadServletResponse#getWriter()
-     */
-    private static class NullWriter extends Writer {
-        @Override
-        public void write(char[] cbuf, int off, int len) {
-        }
-
-        @Override
-        public void write(char[] cbuf) {
-        }
-
-        @Override
-        public void write(int c) {
-        }
-
-        @Override
-        public void write(String str) {
-        }
-
-        @Override
-        public void write(String str, int off, int len) {
-        }
-
-        @Override
-        public void flush() {
-        }
-
-        @Override
-        public void close() {
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/servlets/get/impl/helpers/HeadServletResponse.java b/src/main/java/org/apache/sling/servlets/get/impl/helpers/HeadServletResponse.java
new file mode 100644
index 0000000..f5bed63
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/get/impl/helpers/HeadServletResponse.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.sling.servlets.get.impl.helpers;
+
+
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
+
+import javax.servlet.ServletOutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * The <code>HeadServletResponse</code> is a Sling response wrapper which
+ * ensures that nothing will ever be written by return null writers or
+ * output streams.
+ */
+
+public class HeadServletResponse extends
+        SlingHttpServletResponseWrapper {
+
+    private ServletOutputStream stream;
+
+    private PrintWriter writer;
+
+    public HeadServletResponse(SlingHttpServletResponse wrappedResponse) {
+        super(wrappedResponse);
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() {
+        if (writer != null) {
+            throw new IllegalStateException("Writer already obtained");
+        }
+
+        if (stream == null) {
+            stream = new NullServletOutputStream();
+        }
+
+        return stream;
+    }
+
+    @Override
+    public PrintWriter getWriter() {
+        if (stream != null) {
+            throw new IllegalStateException("OutputStream already obtained");
+        }
+
+        if (writer == null) {
+            writer = new PrintWriter(new NullWriter());
+        }
+
+        return writer;
+    }
+
+    /**
+     * The <code>NullServletOutputStream</code> is a
+     * <code>ServletOutputStream</code> which simply does not write out
+     * anything.
+     *
+     * @see HeadServletResponse#getOutputStream()
+     */
+    private static class NullServletOutputStream extends ServletOutputStream {
+        @Override
+        public void write(int b) {
+        }
+
+        @Override
+        public void write(byte[] b) {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) {
+        }
+    }
+
+    /**
+     * The <code>NullWriter</code> is a <code>Writer</code> which simply does
+     * not write out anything.
+     *
+     * @see HeadServletResponse#getWriter()
+     */
+    private static class NullWriter extends Writer {
+        @Override
+        public void write(char[] cbuf, int off, int len) {
+        }
+
+        @Override
+        public void write(char[] cbuf) {
+        }
+
+        @Override
+        public void write(int c) {
+        }
+
+        @Override
+        public void write(String str) {
+        }
+
+        @Override
+        public void write(String str, int off, int len) {
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() {
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererServlet.java b/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererServlet.java
index 48570cc..e2396c9 100644
--- a/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererServlet.java
+++ b/src/main/java/org/apache/sling/servlets/get/impl/helpers/StreamRendererServlet.java
@@ -50,6 +50,7 @@
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.servlets.HttpConstants;
 import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -96,6 +97,21 @@
             SlingHttpServletResponse response) throws ServletException,
             IOException {
 
+        processRequest(request, response);
+
+    }
+
+    @Override
+    protected void doHead(SlingHttpServletRequest request,
+                         SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        processRequest(request, response);
+    }
+
+    private void processRequest(SlingHttpServletRequest request,
+                                   SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
         // whether this servlet is called as of a request include
         final boolean included = request.getAttribute(SlingConstants.ATTR_REQUEST_SERVLET) != null;
 
@@ -115,11 +131,12 @@
         }
 
         Resource resource = request.getResource();
+
         if (ResourceUtil.isNonExistingResource(resource)) {
             throw new ResourceNotFoundException("No data to render.");
         }
-
         // trailing slash on url means directory listing
+
         if ("/".equals(request.getRequestPathInfo().getSuffix())) {
             renderDirectory(request, response, included);
             return;
@@ -148,6 +165,11 @@
         }
         InputStream stream = resource.adaptTo(InputStream.class);
         if (stream != null) {
+            if (isHeadRequest(request)) {
+                setContentLength(response, resource.getResourceMetadata().getContentLength());
+                setHeaders(resource, response);
+                return;
+            }
 
             streamResource(resource, stream, included, request, response);
 
@@ -182,6 +204,10 @@
             ("/".equals(resource.getResourceResolver().map(resource.getPath())));
     }
 
+    private boolean isHeadRequest(HttpServletRequest request) {
+        return HttpConstants.METHOD_HEAD.equals(request.getMethod());
+    }
+
     /**
      * Returns <code>true</code> if the request has a
      * <code>If-Modified-Since</code> header whose date value is later than the
@@ -294,6 +320,12 @@
             Resource fileRes = resolver.getResource(resource, index);
             if (fileRes != null && !ResourceUtil.isSyntheticResource(fileRes)) {
 
+                setHeaders(fileRes, response);
+
+                if (isHeadRequest(request)) {
+                    return;
+                }
+
                 // include the index resource with no suffix and selectors !
                 RequestDispatcherOptions rdo = new RequestDispatcherOptions();
                 rdo.setReplaceSuffix("");
@@ -307,14 +339,16 @@
                     dispatcher = request.getRequestDispatcher(fileRes, rdo);
                 }
 
-                setHeaders(fileRes, response);
-
                 dispatcher.include(request, response);
                 return;
             }
         }
 
         if (index) {
+            if (isHeadRequest(request)) {
+                setHeaders(resource, response);
+                return;
+            }
             renderIndex(resource, response);
         } else {
             response.sendError(HttpServletResponse.SC_FORBIDDEN);