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);