X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fserver%2Fhandler%2FResourceHandler.java;fp=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fserver%2Fhandler%2FResourceHandler.java;h=10d1e051301e1fad820d9d051f58460458e8acf7;hb=73ef54a38e3930a1a789cdc6b5fa23cdd4c9d086;hp=0000000000000000000000000000000000000000;hpb=515007c7c1351045420669d65b59c08fa46850f2;p=gigi.git diff --git a/lib/jetty/org/eclipse/jetty/server/handler/ResourceHandler.java b/lib/jetty/org/eclipse/jetty/server/handler/ResourceHandler.java new file mode 100644 index 00000000..10d1e051 --- /dev/null +++ b/lib/jetty/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -0,0 +1,613 @@ +// +// ======================================================================== +// 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 java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; + +import javax.servlet.AsyncContext; +import javax.servlet.RequestDispatcher; +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.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.io.WriterOutputStream; +import org.eclipse.jetty.server.HttpOutput; +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.BufferUtil; +import org.eclipse.jetty.util.Callback; +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(); + String _cacheControl; + boolean _directory; + boolean _etags; + int _minMemoryMappedContentLength=-1; + int _minAsyncContentLength=0; + + /* ------------------------------------------------------------ */ + public ResourceHandler() + { + + } + + /* ------------------------------------------------------------ */ + public MimeTypes getMimeTypes() + { + return _mimeTypes; + } + + /* ------------------------------------------------------------ */ + public void setMimeTypes(MimeTypes mimeTypes) + { + _mimeTypes = mimeTypes; + } + + /* ------------------------------------------------------------ */ + /** 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; + } + + /* ------------------------------------------------------------ */ + /** 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. + */ + public int getMinMemoryMappedContentLength() + { + return _minMemoryMappedContentLength; + } + + /* ------------------------------------------------------------ */ + /** 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. + */ + public void setMinMemoryMappedContentLength(int minMemoryMappedFileSize) + { + _minMemoryMappedContentLength = minMemoryMappedFileSize; + } + + /* ------------------------------------------------------------ */ + /** 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. + */ + public int getMinAsyncContentLength() + { + return _minAsyncContentLength; + } + + /* ------------------------------------------------------------ */ + /** 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. + */ + public void setMinAsyncContentLength(int minAsyncContentLength) + { + _minAsyncContentLength = minAsyncContentLength; + } + + /* ------------------------------------------------------------ */ + /** + * @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()); + + 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) + { + _defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css")); + } + 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); + } + } + + /* ------------------------------------------------------------ */ + /** + * @return the cacheControl header to set on all static content. + */ + public String getCacheControl() + { + return _cacheControl; + } + + /* ------------------------------------------------------------ */ + /** + * @param cacheControl the cacheControl header to set on all static content. + */ + public void setCacheControl(String cacheControl) + { + _cacheControl=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(RequestDispatcher.INCLUDE_REQUEST_URI) != null; + if (included != null && included.booleanValue()) + { + servletPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); + pathInfo = (String)request.getAttribute(RequestDispatcher.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) + */ + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + if (baseRequest.isHandled()) + return; + + boolean skipContentBody = false; + + if(!HttpMethod.GET.is(request.getMethod())) + { + if(!HttpMethod.HEAD.is(request.getMethod())) + { + //try another handler + super.handle(target, baseRequest, request, response); + return; + } + skipContentBody = true; + } + + Resource resource = getResource(request); + // If resource is not found + if (resource==null || !resource.exists()) + { + // inject the jetty-dir.css file if it matches + 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; + } + } + + // We are going to serve something + baseRequest.setHandled(true); + + // handle directories + 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; + } + } + + // Handle ETAGS + long last_modified=resource.lastModified(); + String etag=null; + if (_etags) + { + // simple handling of only a single etag + String ifnm = request.getHeader(HttpHeader.IF_NONE_MATCH.asString()); + etag=resource.getWeakETag(); + if (ifnm!=null && resource!=null && ifnm.equals(etag)) + { + response.setStatus(HttpStatus.NOT_MODIFIED_304); + baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag); + return; + } + } + + // Handle if modified since + if (last_modified>0) + { + long if_modified=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); + if (if_modified>0 && last_modified/1000<=if_modified/1000) + { + response.setStatus(HttpStatus.NOT_MODIFIED_304); + return; + } + } + + // set the headers + String mime=_mimeTypes.getMimeByExtension(resource.toString()); + if (mime==null) + mime=_mimeTypes.getMimeByExtension(request.getPathInfo()); + doResponseHeaders(response,resource,mime); + if (_etags) + baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag); + + if(skipContentBody) + return; + + + // Send the content + OutputStream out =null; + try {out = response.getOutputStream();} + catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());} + + // Has the output been wrapped + if (!(out instanceof HttpOutput)) + // Write content via wrapped output + resource.writeTo(out,0,resource.length()); + else + { + // select async by size + int min_async_size=_minAsyncContentLength==0?response.getBufferSize():_minAsyncContentLength; + + if (request.isAsyncSupported() && + min_async_size>0 && + resource.length()>=min_async_size) + { + final AsyncContext async = request.startAsync(); + Callback callback = new Callback() + { + @Override + public void succeeded() + { + async.complete(); + } + + @Override + public void failed(Throwable x) + { + LOG.warn(x.toString()); + LOG.debug(x); + async.complete(); + } + }; + + // Can we use a memory mapped file? + if (_minMemoryMappedContentLength>0 && + resource.length()>_minMemoryMappedContentLength && + resource instanceof FileResource) + { + ByteBuffer buffer = BufferUtil.toMappedBuffer(resource.getFile()); + ((HttpOutput)out).sendContent(buffer,callback); + } + else // Do a blocking write of a channel (if available) or input stream + { + // Close of the channel/inputstream is done by the async sendContent + ReadableByteChannel channel= resource.getReadableByteChannel(); + if (channel!=null) + ((HttpOutput)out).sendContent(channel,callback); + else + ((HttpOutput)out).sendContent(resource.getInputStream(),callback); + } + } + else + { + // Can we use a memory mapped file? + if (_minMemoryMappedContentLength>0 && + resource.length()>_minMemoryMappedContentLength && + resource instanceof FileResource) + { + ByteBuffer buffer = BufferUtil.toMappedBuffer(resource.getFile()); + ((HttpOutput)out).sendContent(buffer); + } + else // Do a blocking write of a channel (if available) or input stream + { + ReadableByteChannel channel= resource.getReadableByteChannel(); + if (channel!=null) + ((HttpOutput)out).sendContent(channel); + else + ((HttpOutput)out).sendContent(resource.getInputStream()); + } + } + } + } + + /* ------------------------------------------------------------ */ + 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) + ((Response)response).setLongContentLength(length); + + if (_cacheControl!=null) + fields.put(HttpHeader.CACHE_CONTROL,_cacheControl); + } + else + { + if (length>Integer.MAX_VALUE) + response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(length)); + else if (length>0) + response.setContentLength((int)length); + + if (_cacheControl!=null) + response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl); + } + } +}