//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.server.handler;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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).
/** * 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 = LoggerFactory.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) throws IOException { if (_welcomes == null) return null; for (int i = 0; i < _welcomes.length; i++) { String welcomeInContext = URIUtil.addPaths(pathInContext, _welcomes[i]); Resource welcome = getResource(welcomeInContext); if (welcome.exists()) return welcomeInContext; } // not found return null; } @Override public void doStart() throws Exception { Context scontext = ContextHandler.getCurrentContext(); _context = (scontext == null ? null : scontext.getContextHandler()); if (_mimeTypes == null) _mimeTypes = _context == null ? new MimeTypes() : _context.getMimeTypes(); _resourceService.setContentFactory(new ResourceContentFactory(this, _mimeTypes, _resourceService.getPrecompressedFormats())); _resourceService.setWelcomeFactory(this); super.doStart(); }
Returns:Returns the resourceBase.
/** * @return Returns the resourceBase. */
public Resource getBaseResource() { if (_baseResource == null) return null; return _baseResource; }
Returns:the cacheControl header to set on all static content.
/** * @return the cacheControl header to set on all static content. */
public String getCacheControl() { return _resourceService.getCacheControl().getValue(); }
Returns:file extensions that signify that a file is gzip compressed. Eg ".svgz"
/** * @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; } @Override public Resource getResource(String path) throws IOException { if (LOG.isDebugEnabled()) LOG.debug("{} getResource({})", _context == null ? _baseResource : _context, path); if (StringUtil.isBlank(path)) { throw new IllegalArgumentException("Path is blank"); } if (!path.startsWith("/")) { throw new IllegalArgumentException("Path reference invalid: " + path); } Resource r = null; if (_baseResource != null) { path = URIUtil.canonicalPath(path); r = _baseResource.addPath(path); if (r.isAlias() && (_context == null || !_context.checkAlias(path, r))) { if (LOG.isDebugEnabled()) LOG.debug("Rejected alias resource={} alias={}", r, r.getAlias()); throw new IllegalStateException("Rejected alias reference: " + path); } } else if (_context != null) { r = _context.getResource(path); if (r != null) return r; } if ((r == null || !r.exists()) && path.endsWith("/jetty-dir.css")) r = getStylesheet(); if (r == null) { throw new FileNotFoundException("Resource: " + path); } return r; }
Returns:Returns the base resource as a string.
/** * @return Returns the base resource as a string. */
public String getResourceBase() { if (_baseResource == null) return null; return _baseResource.toString(); }
Returns:Returns the stylesheet as a Resource.
/** * @return Returns the stylesheet as a Resource. */
public Resource getStylesheet() { if (_stylesheet != null) { return _stylesheet; } else { if (_defaultStylesheet == null) { _defaultStylesheet = getDefaultStylesheet(); } return _defaultStylesheet; } } public static Resource getDefaultStylesheet() { return Resource.newResource(ResourceHandler.class.getResource("/jetty-dir.css")); } public String[] getWelcomeFiles() { return _welcomes; } @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()) && !HttpMethod.HEAD.is(request.getMethod())) { // try another handler super.handle(target, baseRequest, request, response); return; } if (_resourceService.doGet(request, response)) baseRequest.setHandled(true); else // no resource - try other handlers super.handle(target, baseRequest, request, response); }
Returns:If true, range requests and responses are supported
/** * @return If true, range requests and responses are supported */
public boolean isAcceptRanges() { return _resourceService.isAcceptRanges(); }
Returns:If true, directory listings are returned if no welcome file is found. Else 403 Forbidden.
/** * @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.
Returns:true if directories are listed.
/** * Get the directory option. * * @return true if directories are listed. */
public boolean isDirectoriesListed() { return _resourceService.isDirAllowed(); }
Returns:True if ETag processing is done
/** * @return True if ETag processing is done */
public boolean isEtags() { return _resourceService.isEtags(); }
Returns:Precompressed resources formats that can be used to serve compressed variant of resources.
/** * @return Precompressed resources formats that can be used to serve compressed variant of resources. */
public CompressedContentFormat[] getPrecompressedFormats() { return _resourceService.getPrecompressedFormats(); }
Returns:true, only the path info will be applied to the resourceBase
/** * @return true, only the path info will be applied to the resourceBase */
public boolean isPathInfoOnly() { return _resourceService.isPathInfoOnly(); }
Returns:If true, welcome files are redirected rather than forwarded to.
/** * @return If true, welcome files are redirected rather than forwarded to. */
public boolean isRedirectWelcome() { return _resourceService.isRedirectWelcome(); }
Params:
  • acceptRanges – If true, range requests and responses are supported
/** * @param acceptRanges If true, range requests and responses are supported */
public void setAcceptRanges(boolean acceptRanges) { _resourceService.setAcceptRanges(acceptRanges); }
Params:
  • base – The resourceBase to server content from. If null the context resource base is used.
/** * @param base The resourceBase to server content from. If null the * context resource base is used. */
public void setBaseResource(Resource base) { _baseResource = base; }
Params:
  • cacheControl – the cacheControl header to set on all static content.
/** * @param cacheControl the cacheControl header to set on all static content. */
public void setCacheControl(String cacheControl) { _resourceService.setCacheControl(new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cacheControl)); }
Params:
  • dirAllowed – If true, directory listings are returned if no welcome file is found. Else 403 Forbidden.
/** * @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.
Params:
  • directory – true if directories are listed.
/** * Set the directory. * * @param directory true if directories are listed. */
public void setDirectoriesListed(boolean directory) { _resourceService.setDirAllowed(directory); }
Params:
  • etags – True if ETag processing is done
/** * @param etags True if ETag processing is done */
public void setEtags(boolean etags) { _resourceService.setEtags(etags); }
Params:
  • gzipEquivalentFileExtensions – file extensions that signify that a file is gzip compressed. Eg ".svgz"
/** * @param gzipEquivalentFileExtensions file extensions that signify that a file is gzip compressed. Eg ".svgz" */
public void setGzipEquivalentFileExtensions(List<String> gzipEquivalentFileExtensions) { _resourceService.setGzipEquivalentFileExtensions(gzipEquivalentFileExtensions); }
Params:
  • 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.
/** * @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; }
Params:
  • pathInfoOnly – true, only the path info will be applied to the resourceBase
/** * @param pathInfoOnly true, only the path info will be applied to the resourceBase */
public void setPathInfoOnly(boolean pathInfoOnly) { _resourceService.setPathInfoOnly(pathInfoOnly); }
Params:
  • redirectWelcome – If true, welcome files are redirected rather than forwarded to. redirection is always used if the ResourceHandler is not scoped by a ContextHandler
/** * @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); }
Params:
  • resourceBase – The base resource as a string.
/** * @param resourceBase The base resource as a string. */
public void setResourceBase(String resourceBase) { try { setBaseResource(Resource.newResource(resourceBase)); } catch (Exception e) { LOG.warn("Invalid Base Resource reference: {}", resourceBase, e); throw new IllegalArgumentException(resourceBase); } }
Params:
  • stylesheet – The location of the stylesheet to be used as a String.
/** * @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("Invalid StyleSheet reference: {}", stylesheet, e); throw new IllegalArgumentException(stylesheet); } } public void setWelcomeFiles(String[] welcomeFiles) { _welcomes = welcomeFiles; } }