org.eclipse.jetty.server.handler.ResourceHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
//
// ========================================================================
// Copyright (c) 1995-2014 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.io.OutputStream;
import java.net.MalformedURLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.WriterOutputStream;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
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.FileResource;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
/** 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).
*
*
* @org.apache.xbean.XBean
*/
public class ResourceHandler extends HandlerWrapper
{
private static final Logger LOG = Log.getLogger(ResourceHandler.class);
ContextHandler _context;
Resource _baseResource;
Resource _defaultStylesheet;
Resource _stylesheet;
String[] _welcomeFiles={"index.html"};
MimeTypes _mimeTypes = new MimeTypes();
ByteArrayBuffer _cacheControl;
boolean _aliases;
boolean _directory;
boolean _etags;
/* ------------------------------------------------------------ */
public ResourceHandler()
{
}
/* ------------------------------------------------------------ */
public MimeTypes getMimeTypes()
{
return _mimeTypes;
}
/* ------------------------------------------------------------ */
public void setMimeTypes(MimeTypes mimeTypes)
{
_mimeTypes = mimeTypes;
}
/* ------------------------------------------------------------ */
/**
* @return True if resource aliases are allowed.
*/
public boolean isAliases()
{
return _aliases;
}
/* ------------------------------------------------------------ */
/**
* Set if resource aliases (eg symlink, 8.3 names, case insensitivity) are allowed.
* Allowing aliases can significantly increase security vulnerabilities.
* If this handler is deployed inside a ContextHandler, then the
* {@link ContextHandler#isAliases()} takes precedent.
* @param aliases True if aliases are supported.
*/
public void setAliases(boolean aliases)
{
_aliases = aliases;
}
/* ------------------------------------------------------------ */
/** Get the directory option.
* @return true if directories are listed.
*/
public boolean isDirectoriesListed()
{
return _directory;
}
/* ------------------------------------------------------------ */
/** Set the directory.
* @param directory true if directories are listed.
*/
public void setDirectoriesListed(boolean directory)
{
_directory = directory;
}
/* ------------------------------------------------------------ */
/**
* @return True if ETag processing is done
*/
public boolean isEtags()
{
return _etags;
}
/* ------------------------------------------------------------ */
/**
* @param etags True if ETag processing is done
*/
public void setEtags(boolean etags)
{
_etags = etags;
}
/* ------------------------------------------------------------ */
@Override
public void doStart()
throws Exception
{
Context scontext = ContextHandler.getCurrentContext();
_context = (scontext==null?null:scontext.getContextHandler());
if (_context!=null)
_aliases=_context.isAliases();
if (!_aliases && !FileResource.getCheckAliases())
throw new IllegalStateException("Alias checking disabled");
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @return Returns the resourceBase.
*/
public Resource getBaseResource()
{
if (_baseResource==null)
return null;
return _baseResource;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the base resource as a string.
*/
public String getResourceBase()
{
if (_baseResource==null)
return null;
return _baseResource.toString();
}
/* ------------------------------------------------------------ */
/**
* @param base The resourceBase to set.
*/
public void setBaseResource(Resource base)
{
_baseResource=base;
}
/* ------------------------------------------------------------ */
/**
* @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);
}
}
/* ------------------------------------------------------------ */
/**
* @return Returns the stylesheet as a Resource.
*/
public Resource getStylesheet()
{
if(_stylesheet != null)
{
return _stylesheet;
}
else
{
if(_defaultStylesheet == null)
{
try
{
_defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
}
catch(IOException e)
{
LOG.warn(e.toString());
LOG.debug(e);
}
}
return _defaultStylesheet;
}
}
/* ------------------------------------------------------------ */
/**
* @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.toString());
}
}
/* ------------------------------------------------------------ */
/**
* @return the cacheControl header to set on all static content.
*/
public String getCacheControl()
{
return _cacheControl.toString();
}
/* ------------------------------------------------------------ */
/**
* @param cacheControl the cacheControl header to set on all static content.
*/
public void setCacheControl(String cacheControl)
{
_cacheControl=cacheControl==null?null:new ByteArrayBuffer(cacheControl);
}
/* ------------------------------------------------------------ */
/*
*/
public Resource getResource(String path) throws MalformedURLException
{
if (path==null || !path.startsWith("/"))
throw new MalformedURLException(path);
Resource base = _baseResource;
if (base==null)
{
if (_context==null)
return null;
base=_context.getBaseResource();
if (base==null)
return null;
}
try
{
path=URIUtil.canonicalPath(path);
return base.addPath(path);
}
catch(Exception e)
{
LOG.ignore(e);
}
return null;
}
/* ------------------------------------------------------------ */
protected Resource getResource(HttpServletRequest request) throws MalformedURLException
{
String servletPath;
String pathInfo;
Boolean included = request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI) != null;
if (included != null && included.booleanValue())
{
servletPath = (String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
pathInfo = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
if (servletPath == null && pathInfo == null)
{
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
}
else
{
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
return getResource(pathInContext);
}
/* ------------------------------------------------------------ */
public String[] getWelcomeFiles()
{
return _welcomeFiles;
}
/* ------------------------------------------------------------ */
public void setWelcomeFiles(String[] welcomeFiles)
{
_welcomeFiles=welcomeFiles;
}
/* ------------------------------------------------------------ */
protected Resource getWelcome(Resource directory) throws MalformedURLException, IOException
{
for (int i=0;i<_welcomeFiles.length;i++)
{
Resource welcome=directory.addPath(_welcomeFiles[i]);
if (welcome.exists() && !welcome.isDirectory())
return welcome;
}
return null;
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
*/
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (baseRequest.isHandled())
return;
boolean skipContentBody = false;
if(!HttpMethods.GET.equals(request.getMethod()))
{
if(!HttpMethods.HEAD.equals(request.getMethod()))
{
//try another handler
super.handle(target, baseRequest, request, response);
return;
}
skipContentBody = true;
}
Resource resource = getResource(request);
if (resource==null || !resource.exists())
{
if (target.endsWith("/jetty-dir.css"))
{
resource = getStylesheet();
if (resource==null)
return;
response.setContentType("text/css");
}
else
{
//no resource - try other handlers
super.handle(target, baseRequest, request, response);
return;
}
}
if (!_aliases && resource.getAlias()!=null)
{
LOG.info(resource+" aliased to "+resource.getAlias());
return;
}
// We are going to serve something
baseRequest.setHandled(true);
if (resource.isDirectory())
{
if (!request.getPathInfo().endsWith(URIUtil.SLASH))
{
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
return;
}
Resource welcome=getWelcome(resource);
if (welcome!=null && welcome.exists())
resource=welcome;
else
{
doDirectory(request,response,resource);
baseRequest.setHandled(true);
return;
}
}
// set some headers
long last_modified=resource.lastModified();
String etag=null;
if (_etags)
{
// simple handling of only a single etag
String ifnm = request.getHeader(HttpHeaders.IF_NONE_MATCH);
etag=resource.getWeakETag();
if (ifnm!=null && resource!=null && ifnm.equals(etag))
{
response.setStatus(HttpStatus.NOT_MODIFIED_304);
baseRequest.getResponse().getHttpFields().put(HttpHeaders.ETAG_BUFFER,etag);
return;
}
}
if (last_modified>0)
{
long if_modified=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
if (if_modified>0 && last_modified/1000<=if_modified/1000)
{
response.setStatus(HttpStatus.NOT_MODIFIED_304);
return;
}
}
Buffer mime=_mimeTypes.getMimeByExtension(resource.toString());
if (mime==null)
mime=_mimeTypes.getMimeByExtension(request.getPathInfo());
// set the headers
doResponseHeaders(response,resource,mime!=null?mime.toString():null);
response.setDateHeader(HttpHeaders.LAST_MODIFIED,last_modified);
if (_etags)
baseRequest.getResponse().getHttpFields().put(HttpHeaders.ETAG_BUFFER,etag);
if(skipContentBody)
return;
// Send the content
OutputStream out =null;
try {out = response.getOutputStream();}
catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
// See if a short direct method can be used?
if (out instanceof AbstractHttpConnection.Output)
{
// TODO file mapped buffers
((AbstractHttpConnection.Output)out).sendContent(resource.getInputStream());
}
else
{
// Write content normally
resource.writeTo(out,0,resource.length());
}
}
/* ------------------------------------------------------------ */
protected void doDirectory(HttpServletRequest request,HttpServletResponse response, Resource resource)
throws IOException
{
if (_directory)
{
String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().println(listing);
}
else
response.sendError(HttpStatus.FORBIDDEN_403);
}
/* ------------------------------------------------------------ */
/** Set the response headers.
* This method is called to set the response headers such as content type and content length.
* May be extended to add additional headers.
* @param response
* @param resource
* @param mimeType
*/
protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType)
{
if (mimeType!=null)
response.setContentType(mimeType);
long length=resource.length();
if (response instanceof Response)
{
HttpFields fields = ((Response)response).getHttpFields();
if (length>0)
fields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,length);
if (_cacheControl!=null)
fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
}
else
{
if (length>0)
response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(length));
if (_cacheControl!=null)
response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
}
}
}