// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.CompressedContentFormat; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.ResourceContentFactory; import org.eclipse.jetty.server.ResourceService; import org.eclipse.jetty.server.ResourceService.WelcomeFactory; import org.eclipse.jetty.server.handler.ContextHandler.Context; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; /* ------------------------------------------------------------ */ /** * Resource Handler. * * This handle will serve static content and handle If-Modified-Since headers. No caching is done. Requests for resources that do not exist are let pass (Eg no * 404's). * * */ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,WelcomeFactory { private static final Logger LOG = Log.getLogger(ResourceHandler.class); Resource _baseResource; ContextHandler _context; Resource _defaultStylesheet; MimeTypes _mimeTypes; private final ResourceService _resourceService; Resource _stylesheet; String[] _welcomes = { "index.html" }; /* ------------------------------------------------------------ */ public ResourceHandler(ResourceService resourceService) { _resourceService=resourceService; } /* ------------------------------------------------------------ */ public ResourceHandler() { this(new ResourceService() { @Override protected void notFound(HttpServletRequest request, HttpServletResponse response) throws IOException { } }); _resourceService.setGzipEquivalentFileExtensions(new ArrayList<>(Arrays.asList(new String[] { ".svgz" }))); } /* ------------------------------------------------------------ */ @Override public String getWelcomeFile(String pathInContext) { if (_welcomes == null) return null; String welcome_servlet = null; for (int i = 0; i < _welcomes.length; i++) { String welcome_in_context = URIUtil.addPaths(pathInContext,_welcomes[i]); Resource welcome = getResource(welcome_in_context); if (welcome != null && welcome.exists()) return _welcomes[i]; } return welcome_servlet; } /* ------------------------------------------------------------ */ @Override public void doStart() throws Exception { Context scontext = ContextHandler.getCurrentContext(); _context = (scontext == null?null:scontext.getContextHandler()); _mimeTypes = _context == null?new MimeTypes():_context.getMimeTypes(); _resourceService.setContentFactory(new ResourceContentFactory(this,_mimeTypes,_resourceService.getPrecompressedFormats())); _resourceService.setWelcomeFactory(this); super.doStart(); } /* ------------------------------------------------------------ */ /** * @return Returns the resourceBase. */ public Resource getBaseResource() { if (_baseResource == null) return null; return _baseResource; } /* ------------------------------------------------------------ */ /** * @return the cacheControl header to set on all static content. */ public String getCacheControl() { return _resourceService.getCacheControl().getValue(); } /* ------------------------------------------------------------ */ /** * @return file extensions that signify that a file is gzip compressed. Eg ".svgz" */ public List<String> getGzipEquivalentFileExtensions() { return _resourceService.getGzipEquivalentFileExtensions(); } /* ------------------------------------------------------------ */ public MimeTypes getMimeTypes() { return _mimeTypes; } /* ------------------------------------------------------------ */ /** * Get the minimum content length for async handling. * * @return The minimum size in bytes of the content before asynchronous handling is used, or -1 for no async handling or 0 (default) for using * {@link HttpServletResponse#getBufferSize()} as the minimum length. */ @Deprecated public int getMinAsyncContentLength() { return -1; } /* ------------------------------------------------------------ */ /** * Get minimum memory mapped file content length. * * @return the minimum size in bytes of a file resource that will be served using a memory mapped buffer, or -1 (default) for no memory mapped buffers. */ @Deprecated public int getMinMemoryMappedContentLength() { return -1; } /* ------------------------------------------------------------ */ /* */ @Override public Resource getResource(String path) { if (LOG.isDebugEnabled()) LOG.debug("{} getResource({})",_context == null?_baseResource:_context,_baseResource,path); if (path == null || !path.startsWith("/")) return null; try { Resource r = null; if (_baseResource != null) { path = URIUtil.canonicalPath(path); r = _baseResource.addPath(path); if (r != null && r.isAlias() && (_context == null || !_context.checkAlias(path,r))) { if (LOG.isDebugEnabled()) LOG.debug("resource={} alias={}",r,r.getAlias()); return null; } } else if (_context != null) r = _context.getResource(path); if ((r == null || !r.exists()) && path.endsWith("/jetty-dir.css")) r = getStylesheet(); return r; } catch (Exception e) { LOG.debug(e); } return null; } /* ------------------------------------------------------------ */ /** * @return Returns the base resource as a string. */ public String getResourceBase() { if (_baseResource == null) return null; return _baseResource.toString(); } /* ------------------------------------------------------------ */ /** * @return Returns the stylesheet as a Resource. */ public Resource getStylesheet() { if (_stylesheet != null) { return _stylesheet; } else { if (_defaultStylesheet == null) { _defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css")); } return _defaultStylesheet; } } /* ------------------------------------------------------------ */ public String[] getWelcomeFiles() { return _welcomes; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) */ @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (baseRequest.isHandled()) return; if (!HttpMethod.GET.is(request.getMethod())) { if (!HttpMethod.HEAD.is(request.getMethod())) { // try another handler super.handle(target,baseRequest,request,response); return; } } _resourceService.doGet(request,response); if (response.isCommitted()) baseRequest.setHandled(true); else // no resource - try other handlers super.handle(target,baseRequest,request,response); } /* ------------------------------------------------------------ */ /** * @return If true, range requests and responses are supported */ public boolean isAcceptRanges() { return _resourceService.isAcceptRanges(); } /** * @return If true, directory listings are returned if no welcome file is found. Else 403 Forbidden. */ public boolean isDirAllowed() { return _resourceService.isDirAllowed(); } /* ------------------------------------------------------------ */ /** * Get the directory option. * * @return true if directories are listed. */ public boolean isDirectoriesListed() { return _resourceService.isDirAllowed(); } /* ------------------------------------------------------------ */ /** * @return True if ETag processing is done */ public boolean isEtags() { return _resourceService.isEtags(); } /* ------------------------------------------------------------ */ /** * @return If set to true, then static content will be served as gzip content encoded if a matching resource is found ending with ".gz" */ @Deprecated public boolean isGzip() { for (CompressedContentFormat formats : _resourceService.getPrecompressedFormats()) { if (CompressedContentFormat.GZIP._encoding.equals(formats._encoding)) return true; } return false; } /** * @return Precompressed resources formats that can be used to serve compressed variant of resources. */ public CompressedContentFormat[] getPrecompressedFormats() { return _resourceService.getPrecompressedFormats(); } /* ------------------------------------------------------------ */ /** * @return true, only the path info will be applied to the resourceBase */ public boolean isPathInfoOnly() { return _resourceService.isPathInfoOnly(); } /* ------------------------------------------------------------ */ /** * @return If true, welcome files are redirected rather than forwarded to. */ public boolean isRedirectWelcome() { return _resourceService.isRedirectWelcome(); } /* ------------------------------------------------------------ */ /** * @param acceptRanges If true, range requests and responses are supported */ public void setAcceptRanges(boolean acceptRanges) { _resourceService.setAcceptRanges(acceptRanges); } /* ------------------------------------------------------------ */ /** * @param base The resourceBase to server content from. If null the * context resource base is used. */ public void setBaseResource(Resource base) { _baseResource = base; } /* ------------------------------------------------------------ */ /** * @param cacheControl * the cacheControl header to set on all static content. */ public void setCacheControl(String cacheControl) { _resourceService.setCacheControl(new PreEncodedHttpField(HttpHeader.CACHE_CONTROL,cacheControl)); } /* ------------------------------------------------------------ */ /** * @param dirAllowed * If true, directory listings are returned if no welcome file is found. Else 403 Forbidden. */ public void setDirAllowed(boolean dirAllowed) { _resourceService.setDirAllowed(dirAllowed); } /* ------------------------------------------------------------ */ /** * Set the directory. * * @param directory * true if directories are listed. */ public void setDirectoriesListed(boolean directory) { _resourceService.setDirAllowed(directory); } /* ------------------------------------------------------------ */ /** * @param etags * True if ETag processing is done */ public void setEtags(boolean etags) { _resourceService.setEtags(etags); } /* ------------------------------------------------------------ */ /** * @param gzip * If set to true, then static content will be served as gzip content encoded if a matching resource is found ending with ".gz" */ @Deprecated public void setGzip(boolean gzip) { setPrecompressedFormats(gzip?new CompressedContentFormat[]{CompressedContentFormat.GZIP}:new CompressedContentFormat[0]); } /* ------------------------------------------------------------ */ /** * @param gzipEquivalentFileExtensions file extensions that signify that a file is gzip compressed. Eg ".svgz" */ public void setGzipEquivalentFileExtensions(List<String> gzipEquivalentFileExtensions) { _resourceService.setGzipEquivalentFileExtensions(gzipEquivalentFileExtensions); } /** * @param precompressedFormats * The list of precompresed formats to serve in encoded format if matching resource found. * For example serve gzip encoded file if ".gz" suffixed resource is found. */ public void setPrecompressedFormats(CompressedContentFormat[] precompressedFormats) { _resourceService.setPrecompressedFormats(precompressedFormats); } /* ------------------------------------------------------------ */ public void setMimeTypes(MimeTypes mimeTypes) { _mimeTypes = mimeTypes; } /* ------------------------------------------------------------ */ /** * Set the minimum content length for async handling. * * @param minAsyncContentLength * The minimum size in bytes of the content before asynchronous handling is used, or -1 for no async handling or 0 for using * {@link HttpServletResponse#getBufferSize()} as the minimum length. */ @Deprecated public void setMinAsyncContentLength(int minAsyncContentLength) { } /* ------------------------------------------------------------ */ /** * Set minimum memory mapped file content length. * * @param minMemoryMappedFileSize * the minimum size in bytes of a file resource that will be served using a memory mapped buffer, or -1 for no memory mapped buffers. */ @Deprecated public void setMinMemoryMappedContentLength(int minMemoryMappedFileSize) { } /* ------------------------------------------------------------ */ /** * @param pathInfoOnly * true, only the path info will be applied to the resourceBase */ public void setPathInfoOnly(boolean pathInfoOnly) { _resourceService.setPathInfoOnly(pathInfoOnly); } /* ------------------------------------------------------------ */ /** * @param redirectWelcome * If true, welcome files are redirected rather than forwarded to. * redirection is always used if the ResourceHandler is not scoped by * a ContextHandler */ public void setRedirectWelcome(boolean redirectWelcome) { _resourceService.setRedirectWelcome(redirectWelcome); } /* ------------------------------------------------------------ */ /** * @param resourceBase * The base resource as a string. */ public void setResourceBase(String resourceBase) { try { setBaseResource(Resource.newResource(resourceBase)); } catch (Exception e) { LOG.warn(e.toString()); LOG.debug(e); throw new IllegalArgumentException(resourceBase); } } /* ------------------------------------------------------------ */ /** * @param stylesheet * The location of the stylesheet to be used as a String. */ public void setStylesheet(String stylesheet) { try { _stylesheet = Resource.newResource(stylesheet); if (!_stylesheet.exists()) { LOG.warn("unable to find custom stylesheet: " + stylesheet); _stylesheet = null; } } catch (Exception e) { LOG.warn(e.toString()); LOG.debug(e); throw new IllegalArgumentException(stylesheet); } } /* ------------------------------------------------------------ */ public void setWelcomeFiles(String[] welcomeFiles) { _welcomes = welcomeFiles; } }