//
// ========================================================================
// 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;
}
}