diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index adb4809fb72..07fe31be8ce 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -96,17 +96,43 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H throws ServletException, IOException { checkAndPrepare(request, response, true); + + // check whether a matching resource exists Resource resource = getResource(request); if (resource == null) { + logger.debug("No matching resource found - returning 404"); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } - setHeaders(resource, response); - if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified()) || - METHOD_HEAD.equals(request.getMethod())) { + + // check the resource's media type + MediaType mediaType = getMediaType(resource); + if (mediaType != null) { + if (logger.isDebugEnabled()) { + logger.debug("Determined media type [" + mediaType + "] for " + resource); + } + } + else { + if (logger.isDebugEnabled()) { + logger.debug("No media type found for " + resource + " - returning 404"); + } + response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } - writeContent(resource, response); + + // header phase + setHeaders(response, resource, mediaType); + if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) { + logger.debug("Resource not modified - returning 304"); + return; + } + + // content phase + if (METHOD_HEAD.equals(request.getMethod())) { + logger.trace("HEAD request - skipping content"); + return; + } + writeContent(response, resource); } protected Resource getResource(HttpServletRequest request) { @@ -115,42 +141,72 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H throw new IllegalStateException("Required request attribute '" + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set"); } + if (!StringUtils.hasText(path) || path.contains("WEB-INF") || path.contains("META-INF")) { + if (logger.isDebugEnabled()) { + logger.debug("Ignoring invalid resource path [" + path + "]"); + } return null; } - for (Resource resourcePath : this.locations) { + + for (Resource location : this.locations) { try { - Resource resource = resourcePath.createRelative(path); + if (logger.isDebugEnabled()) { + logger.debug("Trying relative path [" + path + "] against base location: " + location); + } + Resource resource = location.createRelative(path); if (resource.exists() && resource.isReadable()) { + if (logger.isDebugEnabled()) { + logger.debug("Found matching resource: " + resource); + } return resource; } + else if (logger.isTraceEnabled()) { + logger.trace("Relative resource doesn't exist or isn't readable: " + resource); + } } catch (IOException ex) { - // resource not found - return null; + logger.debug("Failed to create relative resource - trying next resource location", ex); } } return null; } - protected void setHeaders(Resource resource, HttpServletResponse response) throws IOException { - MediaType mediaType = getMediaType(resource); - if (mediaType != null) { - response.setContentType(mediaType.toString()); - } - long length = resource.contentLength(); - if (length > Integer.MAX_VALUE) { - throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource); - } - response.setContentLength((int) length); - } - + /** + * Determine an appropriate media type for the given resource. + * @param resource the resource to check + * @return the corresponding media type, or null if none found + */ protected MediaType getMediaType(Resource resource) { String mimeType = getServletContext().getMimeType(resource.getFilename()); return (StringUtils.hasText(mimeType) ? MediaType.parseMediaType(mimeType) : null); } - protected void writeContent(Resource resource, HttpServletResponse response) throws IOException { + /** + * Set headers on the given servlet response. + * Called for GET requests as well as HEAD requests. + * @param response current servlet response + * @param resource the identified resource (never null) + * @param mediaType the resource's media type (never null) + * @throws IOException in case of errors while setting the headers + */ + protected void setHeaders(HttpServletResponse response, Resource resource, MediaType mediaType) throws IOException { + long length = resource.contentLength(); + if (length > Integer.MAX_VALUE) { + throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource); + } + response.setContentLength((int) length); + response.setContentType(mediaType.toString()); + } + + /** + * Write the actual content out to the given servlet response, + * streaming the resource's content. + * @param response current servlet response + * @param resource the identified resource (never null) + * @throws IOException in case of errors while writing the content + */ + protected void writeContent(HttpServletResponse response, Resource resource) throws IOException { FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream()); }