All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.servlet.DefaultServlet Maven / Gradle / Ivy

//
//  ========================================================================
//  Copyright (c) 1995-2016 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.servlet;

import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
import static org.eclipse.jetty.http.GzipHttpContent.removeGzipFromETag;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.DateParser;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PathMap.MappedEntry;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.io.WriterOutputStream;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.InclusiveByteRange;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.ResourceCache;
import org.eclipse.jetty.server.ResourceContentFactory;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartOutputStream;
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.ResourceCollection;
import org.eclipse.jetty.util.resource.ResourceFactory;


/** 
 * The default servlet.
 * 

* This servlet, normally mapped to /, provides the handling for static * content, OPTION and TRACE methods for the context. * The following initParameters are supported, these can be set either * on the servlet itself or as ServletContext initParameters with a prefix * of org.eclipse.jetty.servlet.Default. : *

 *  acceptRanges      If true, range requests and responses are
 *                    supported
 *
 *  dirAllowed        If true, directory listings are returned if no
 *                    welcome file is found. Else 403 Forbidden.
 *
 *  welcomeServlets   If true, attempt to dispatch to welcome files
 *                    that are servlets, but only after no matching static
 *                    resources could be found. If false, then a welcome
 *                    file must exist on disk. If "exact", then exact
 *                    servlet matches are supported without an existing file.
 *                    Default is true.
 *
 *                    This must be false if you want directory listings,
 *                    but have index.jsp in your welcome file list.
 *
 *  redirectWelcome   If true, welcome files are redirected rather than
 *                    forwarded to.
 *
 *  gzip              If set to true, then static content will be served as
 *                    gzip content encoded if a matching resource is
 *                    found ending with ".gz"
 *
 *  resourceBase      Set to replace the context resource base
 *
 *  resourceCache     If set, this is a context attribute name, which the servlet
 *                    will use to look for a shared ResourceCache instance.
 *
 *  relativeResourceBase
 *                    Set with a pathname relative to the base of the
 *                    servlet context root. Useful for only serving static content out
 *                    of only specific subdirectories.
 *
 *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
 *
 *  stylesheet	      Set with the location of an optional stylesheet that will be used
 *                    to decorate the directory listing html.
 *
 *  etags             If True, weak etags will be generated and handled.
 *
 *  maxCacheSize      The maximum total size of the cache or 0 for no cache.
 *  maxCachedFileSize The maximum size of a file to cache
 *  maxCachedFiles    The maximum number of files to cache
 *
 *  useFileMappedBuffer
 *                    If set to true, it will use mapped file buffer to serve static content
 *                    when using NIO connector. Setting this value to false means that
 *                    a direct buffer will be used instead of a mapped file buffer.
 *                    This is set to false by default by this class, but may be overridden
 *                    by eg webdefault.xml 
 *
 *  cacheControl      If set, all static content will have this value set as the cache-control
 *                    header.
 *                    
 * otherGzipFileExtensions
 *                    Other file extensions that signify that a file is gzip compressed. Eg ".svgz"
 *
 *
 * 
* */ public class DefaultServlet extends HttpServlet implements ResourceFactory { private static final Logger LOG = Log.getLogger(DefaultServlet.class); private static final long serialVersionUID = 4930458713846881193L; private static final PreEncodedHttpField ACCEPT_RANGES = new PreEncodedHttpField(HttpHeader.ACCEPT_RANGES, "bytes"); private ServletContext _servletContext; private ContextHandler _contextHandler; private boolean _acceptRanges=true; private boolean _dirAllowed=true; private boolean _welcomeServlets=false; private boolean _welcomeExactServlets=false; private boolean _redirectWelcome=false; private boolean _gzip=false; private boolean _pathInfoOnly=false; private boolean _etags=false; private Resource _resourceBase; private ResourceCache _cache; private HttpContent.Factory _contentFactory; private MimeTypes _mimeTypes; private String[] _welcomes; private Resource _stylesheet; private boolean _useFileMappedBuffer=false; private HttpField _cacheControl; private String _relativeResourceBase; private ServletHandler _servletHandler; private ServletHolder _defaultHolder; private List _gzipEquivalentFileExtensions; /* ------------------------------------------------------------ */ @Override public void init() throws UnavailableException { _servletContext=getServletContext(); _contextHandler = initContextHandler(_servletContext); _mimeTypes = _contextHandler.getMimeTypes(); _welcomes = _contextHandler.getWelcomeFiles(); if (_welcomes==null) _welcomes=new String[] {"index.html","index.jsp"}; _acceptRanges=getInitBoolean("acceptRanges",_acceptRanges); _dirAllowed=getInitBoolean("dirAllowed",_dirAllowed); _redirectWelcome=getInitBoolean("redirectWelcome",_redirectWelcome); _gzip=getInitBoolean("gzip",_gzip); _pathInfoOnly=getInitBoolean("pathInfoOnly",_pathInfoOnly); if ("exact".equals(getInitParameter("welcomeServlets"))) { _welcomeExactServlets=true; _welcomeServlets=false; } else _welcomeServlets=getInitBoolean("welcomeServlets", _welcomeServlets); _useFileMappedBuffer=getInitBoolean("useFileMappedBuffer",_useFileMappedBuffer); _relativeResourceBase = getInitParameter("relativeResourceBase"); String rb=getInitParameter("resourceBase"); if (rb!=null) { if (_relativeResourceBase!=null) throw new UnavailableException("resourceBase & relativeResourceBase"); try{_resourceBase=_contextHandler.newResource(rb);} catch (Exception e) { LOG.warn(Log.EXCEPTION,e); throw new UnavailableException(e.toString()); } } String css=getInitParameter("stylesheet"); try { if(css!=null) { _stylesheet = Resource.newResource(css); if(!_stylesheet.exists()) { LOG.warn("!" + css); _stylesheet = null; } } if(_stylesheet == null) { _stylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css")); } } catch(Exception e) { LOG.warn(e.toString()); LOG.debug(e); } String cc=getInitParameter("cacheControl"); if (cc!=null) _cacheControl=new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cc); String resourceCache = getInitParameter("resourceCache"); int max_cache_size=getInitInt("maxCacheSize", -2); int max_cached_file_size=getInitInt("maxCachedFileSize", -2); int max_cached_files=getInitInt("maxCachedFiles", -2); if (resourceCache!=null) { if (max_cache_size!=-1 || max_cached_file_size!= -2 || max_cached_files!=-2) LOG.debug("ignoring resource cache configuration, using resourceCache attribute"); if (_relativeResourceBase!=null || _resourceBase!=null) throw new UnavailableException("resourceCache specified with resource bases"); _cache=(ResourceCache)_servletContext.getAttribute(resourceCache); if (LOG.isDebugEnabled()) LOG.debug("Cache {}={}",resourceCache,_contentFactory); } _etags = getInitBoolean("etags",_etags); try { if (_cache==null && (max_cached_files!=-2 || max_cache_size!=-2 || max_cached_file_size!=-2)) { _cache = new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags,_gzip); if (max_cache_size>=0) _cache.setMaxCacheSize(max_cache_size); if (max_cached_file_size>=-1) _cache.setMaxCachedFileSize(max_cached_file_size); if (max_cached_files>=-1) _cache.setMaxCachedFiles(max_cached_files); _servletContext.setAttribute(resourceCache==null?"resourceCache":resourceCache,_cache); } } catch (Exception e) { LOG.warn(Log.EXCEPTION,e); throw new UnavailableException(e.toString()); } if (_cache!=null) _contentFactory=_cache; else { _contentFactory=new ResourceContentFactory(this,_mimeTypes,_gzip); if (resourceCache!=null) _servletContext.setAttribute(resourceCache,_contentFactory); } _gzipEquivalentFileExtensions = new ArrayList(); String otherGzipExtensions = getInitParameter("otherGzipFileExtensions"); if (otherGzipExtensions != null) { //comma separated list StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false); while (tok.hasMoreTokens()) { String s = tok.nextToken().trim(); _gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s)); } } else { //.svgz files are gzipped svg files and must be served with Content-Encoding:gzip _gzipEquivalentFileExtensions.add(".svgz"); } _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class); for (ServletHolder h :_servletHandler.getServlets()) if (h.getServletInstance()==this) _defaultHolder=h; if (LOG.isDebugEnabled()) LOG.debug("resource base = "+_resourceBase); } /** * Compute the field _contextHandler.
* In the case where the DefaultServlet is deployed on the HttpService it is likely that * this method needs to be overwritten to unwrap the ServletContext facade until we reach * the original jetty's ContextHandler. * @param servletContext The servletContext of this servlet. * @return the jetty's ContextHandler for this servletContext. */ protected ContextHandler initContextHandler(ServletContext servletContext) { ContextHandler.Context scontext=ContextHandler.getCurrentContext(); if (scontext==null) { if (servletContext instanceof ContextHandler.Context) return ((ContextHandler.Context)servletContext).getContextHandler(); else throw new IllegalArgumentException("The servletContext " + servletContext + " " + servletContext.getClass().getName() + " is not " + ContextHandler.Context.class.getName()); } else return ContextHandler.getCurrentContext().getContextHandler(); } /* ------------------------------------------------------------ */ @Override public String getInitParameter(String name) { String value=getServletContext().getInitParameter("org.eclipse.jetty.servlet.Default."+name); if (value==null) value=super.getInitParameter(name); return value; } /* ------------------------------------------------------------ */ private boolean getInitBoolean(String name, boolean dft) { String value=getInitParameter(name); if (value==null || value.length()==0) return dft; return (value.startsWith("t")|| value.startsWith("T")|| value.startsWith("y")|| value.startsWith("Y")|| value.startsWith("1")); } /* ------------------------------------------------------------ */ private int getInitInt(String name, int dft) { String value=getInitParameter(name); if (value==null) value=getInitParameter(name); if (value!=null && value.length()>0) return Integer.parseInt(value); return dft; } /* ------------------------------------------------------------ */ /** get Resource to serve. * Map a path to a resource. The default implementation calls * HttpContext.getResource but derived servlets may provide * their own mapping. * @param pathInContext The path to find a resource for. * @return The resource to serve. */ @Override public Resource getResource(String pathInContext) { Resource r=null; if (_relativeResourceBase!=null) pathInContext=URIUtil.addPaths(_relativeResourceBase,pathInContext); try { if (_resourceBase!=null) { r = _resourceBase.addPath(pathInContext); if (!_contextHandler.checkAlias(pathInContext,r)) r=null; } else if (_servletContext instanceof ContextHandler.Context) { r = _contextHandler.getResource(pathInContext); } else { URL u = _servletContext.getResource(pathInContext); r = _contextHandler.newResource(u); } if (LOG.isDebugEnabled()) LOG.debug("Resource "+pathInContext+"="+r); } catch (IOException e) { LOG.ignore(e); } if((r==null || !r.exists()) && pathInContext.endsWith("/jetty-dir.css")) r=_stylesheet; return r; } /* ------------------------------------------------------------ */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String servletPath=null; String pathInfo=null; Enumeration reqRanges = null; boolean included =request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null; if (included) { servletPath=(String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); pathInfo=(String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); if (servletPath==null) { servletPath=request.getServletPath(); pathInfo=request.getPathInfo(); } } else { servletPath = _pathInfoOnly?"/":request.getServletPath(); pathInfo = request.getPathInfo(); // Is this a Range request? reqRanges = request.getHeaders(HttpHeader.RANGE.asString()); if (!hasDefinedRange(reqRanges)) reqRanges = null; } String pathInContext=URIUtil.addPaths(servletPath,pathInfo); boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH); boolean gzippable=_gzip && !endsWithSlash && !included && reqRanges==null; HttpContent content=null; boolean release_content=true; try { // Find the content content=_contentFactory.getContent(pathInContext,response.getBufferSize()); if (LOG.isDebugEnabled()) LOG.info("content={}",content); // Not found? if (content==null || !content.getResource().exists()) { if (included) throw new FileNotFoundException("!" + pathInContext); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Directory? if (content.getResource().isDirectory()) { sendWelcome(content,pathInContext,endsWithSlash,included,request,response); return; } // Strip slash? if (endsWithSlash && pathInContext.length()>1) { String q=request.getQueryString(); pathInContext=pathInContext.substring(0,pathInContext.length()-1); if (q!=null&&q.length()!=0) pathInContext+="?"+q; response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext))); return; } // Conditional response? if (!included && !passConditionalHeaders(request,response,content)) return; // Gzip? HttpContent gzip_content = gzippable?content.getGzipContent():null; if (gzip_content!=null) { // Tell caches that response may vary by accept-encoding response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString()); // Does the client accept gzip? String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString()); if (accept!=null && accept.indexOf("gzip")>=0) { if (LOG.isDebugEnabled()) LOG.debug("gzip={}",gzip_content); content=gzip_content; } } // TODO this should be done by HttpContent#getContentEncoding if (isGzippedContent(pathInContext)) response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip"); // Send the data release_content=sendData(request,response,included,content,reqRanges); } catch(IllegalArgumentException e) { LOG.warn(Log.EXCEPTION,e); if(!response.isCommitted()) response.sendError(500, e.getMessage()); } finally { if (release_content) { if (content!=null) content.release(); } } } protected void sendWelcome(HttpContent content, String pathInContext, boolean endsWithSlash, boolean included, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Redirect to directory if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null)) { StringBuffer buf=request.getRequestURL(); synchronized(buf) { int param=buf.lastIndexOf(";"); if (param<0) buf.append('/'); else buf.insert(param,'/'); String q=request.getQueryString(); if (q!=null&&q.length()!=0) { buf.append('?'); buf.append(q); } response.setContentLength(0); response.sendRedirect(response.encodeRedirectURL(buf.toString())); } return; } // look for a welcome file String welcome=getWelcomeFile(pathInContext); if (welcome!=null) { if (LOG.isDebugEnabled()) LOG.debug("welcome={}",welcome); if (_redirectWelcome) { // Redirect to the index response.setContentLength(0); String q=request.getQueryString(); if (q!=null&&q.length()!=0) response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q)); else response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome))); } else { // Forward to the index RequestDispatcher dispatcher=request.getRequestDispatcher(welcome); if (dispatcher!=null) { if (included) dispatcher.include(request,response); else { request.setAttribute("org.eclipse.jetty.server.welcome",welcome); dispatcher.forward(request,response); } } } return; } if (included || passConditionalHeaders(request,response, content)) sendDirectory(request,response,content.getResource(),pathInContext); } /* ------------------------------------------------------------ */ protected boolean isGzippedContent(String path) { if (path == null) return false; for (String suffix:_gzipEquivalentFileExtensions) if (path.endsWith(suffix)) return true; return false; } /* ------------------------------------------------------------ */ private boolean hasDefinedRange(Enumeration reqRanges) { return (reqRanges!=null && reqRanges.hasMoreElements()); } /* ------------------------------------------------------------ */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } /* ------------------------------------------------------------ */ /* (non-Javadoc) * @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } /* ------------------------------------------------------------ */ @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Allow", "GET,HEAD,POST,OPTIONS"); } /* ------------------------------------------------------------ */ /** * Finds a matching welcome file for the supplied {@link Resource}. This will be the first entry in the list of * configured {@link #_welcomes welcome files} that existing within the directory referenced by the Resource. * If the resource is not a directory, or no matching file is found, then it may look for a valid servlet mapping. * If there is none, then null is returned. * The list of welcome files is read from the {@link ContextHandler} for this servlet, or * "index.jsp" , "index.html" if that is null. * @param resource * @return The path of the matching welcome file in context or null. * @throws IOException * @throws MalformedURLException */ private String getWelcomeFile(String pathInContext) throws MalformedURLException, IOException { 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]; if ((_welcomeServlets || _welcomeExactServlets) && welcome_servlet==null) { MappedEntry entry=_servletHandler.getHolderEntry(welcome_in_context); if (entry!=null && entry.getValue()!=_defaultHolder && (_welcomeServlets || (_welcomeExactServlets && entry.getKey().equals(welcome_in_context)))) welcome_servlet=welcome_in_context; } } return welcome_servlet; } /* ------------------------------------------------------------ */ /* Check modification date headers. */ protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, HttpContent content) throws IOException { try { String ifm=null; String ifnm=null; String ifms=null; long ifums=-1; if (request instanceof Request) { // Find multiple fields by iteration as an optimization HttpFields fields = ((Request)request).getHttpFields(); for (int i=fields.size();i-->0;) { HttpField field=fields.getField(i); if (field.getHeader() != null) { switch (field.getHeader()) { case IF_MATCH: ifm=field.getValue(); break; case IF_NONE_MATCH: ifnm=field.getValue(); break; case IF_MODIFIED_SINCE: ifms=field.getValue(); break; case IF_UNMODIFIED_SINCE: ifums=DateParser.parseDate(field.getValue()); break; default: } } } } else { ifm=request.getHeader(HttpHeader.IF_MATCH.asString()); ifnm=request.getHeader(HttpHeader.IF_NONE_MATCH.asString()); ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); ifums=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString()); } if (!HttpMethod.HEAD.is(request.getMethod())) { if (_etags) { String etag=content.getETagValue(); if (ifm!=null) { boolean match=false; if (etag!=null) { QuotedCSV quoted = new QuotedCSV(true,ifm); for (String tag : quoted) { if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag))) { match=true; break; } } } if (!match) { response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } if (ifnm!=null && etag!=null) { // Handle special case of exact match OR gzip exact match if (etag.equals(ifnm) || ifnm.endsWith(ETAG_GZIP_QUOTE) && ifnm.indexOf(',')<0 && etag.equals(removeGzipFromETag(etag))) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.setHeader(HttpHeader.ETAG.asString(),ifnm); return false; } // Handle list of tags QuotedCSV quoted = new QuotedCSV(true,ifnm); for (String tag : quoted) { if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag))) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.setHeader(HttpHeader.ETAG.asString(),tag); return false; } } // If etag requires content to be served, then do not check if-modified-since return true; } } // Handle if modified since if (ifms!=null) { //Get jetty's Response impl String mdlm=content.getLastModifiedValue(); if (mdlm!=null && ifms.equals(mdlm)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); if (_etags) response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue()); response.flushBuffer(); return false; } long ifmsl=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); if (ifmsl!=-1 && content.getResource().lastModified()/1000 <= ifmsl/1000) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); if (_etags) response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue()); response.flushBuffer(); return false; } } // Parse the if[un]modified dates and compare to resource if (ifums!=-1 && content.getResource().lastModified()/1000 > ifums/1000) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } } catch(IllegalArgumentException iae) { if(!response.isCommitted()) response.sendError(400, iae.getMessage()); throw iae; } return true; } /* ------------------------------------------------------------------- */ protected void sendDirectory(HttpServletRequest request, HttpServletResponse response, Resource resource, String pathInContext) throws IOException { if (!_dirAllowed) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } byte[] data=null; String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH); //If the DefaultServlet has a resource base set, use it if (_resourceBase != null) { // handle ResourceCollection if (_resourceBase instanceof ResourceCollection) resource=_resourceBase.addPath(pathInContext); } //Otherwise, try using the resource base of its enclosing context handler else if (_contextHandler.getBaseResource() instanceof ResourceCollection) resource=_contextHandler.getBaseResource().addPath(pathInContext); String dir = resource.getListHTML(base,pathInContext.length()>1); if (dir==null) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "No directory"); return; } data=dir.getBytes("utf-8"); response.setContentType("text/html;charset=utf-8"); response.setContentLength(data.length); response.getOutputStream().write(data); } /* ------------------------------------------------------------ */ protected boolean sendData(HttpServletRequest request, HttpServletResponse response, boolean include, final HttpContent content, Enumeration reqRanges) throws IOException { final long content_length = content.getContentLengthValue(); // Get the output stream (or writer) OutputStream out =null; boolean written; try { out = response.getOutputStream(); // has something already written to the response? written = out instanceof HttpOutput ? ((HttpOutput)out).isWritten() : true; } catch(IllegalStateException e) { out = new WriterOutputStream(response.getWriter()); written=true; // there may be data in writer buffer, so assume written } if (LOG.isDebugEnabled()) LOG.debug(String.format("sendData content=%s out=%s async=%b",content,out,request.isAsyncSupported())); if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0) { // if there were no ranges, send entire entity if (include) { // write without headers content.getResource().writeTo(out,0,content_length); } // else if we can't do a bypass write because of wrapping else if (written || !(out instanceof HttpOutput)) { // write normally putHeaders(response,content,written?-1:0); ByteBuffer buffer = content.getIndirectBuffer(); if (buffer!=null) BufferUtil.writeTo(buffer,out); else content.getResource().writeTo(out,0,content_length); } // else do a bypass write else { // write the headers putHeaders(response,content,0); // write the content asynchronously if supported if (request.isAsyncSupported()) { final AsyncContext context = request.startAsync(); context.setTimeout(0); ((HttpOutput)out).sendContent(content,new Callback() { @Override public void succeeded() { context.complete(); content.release(); } @Override public void failed(Throwable x) { if (x instanceof IOException) LOG.debug(x); else LOG.warn(x); context.complete(); content.release(); } @Override public String toString() { return String.format("DefaultServlet@%x$CB", DefaultServlet.this.hashCode()); } }); return false; } // otherwise write content blocking ((HttpOutput)out).sendContent(content); } } else { // Parse the satisfiable ranges List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length); // if there are no satisfiable ranges, send 416 response if (ranges==null || ranges.size()==0) { putHeaders(response,content,0); response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); response.setHeader(HttpHeader.CONTENT_RANGE.asString(), InclusiveByteRange.to416HeaderRangeString(content_length)); content.getResource().writeTo(out,0,content_length); return true; } // if there is only a single valid range (must be satisfiable // since were here now), send that range with a 216 response if ( ranges.size()== 1) { InclusiveByteRange singleSatisfiableRange = ranges.get(0); long singleLength = singleSatisfiableRange.getSize(content_length); putHeaders(response,content,singleLength); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); if (!response.containsHeader(HttpHeader.DATE.asString())) response.addDateHeader(HttpHeader.DATE.asString(),System.currentTimeMillis()); response.setHeader(HttpHeader.CONTENT_RANGE.asString(), singleSatisfiableRange.toHeaderRangeString(content_length)); content.getResource().writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength); return true; } // multiple non-overlapping valid ranges cause a multipart // 216 response which does not require an overall // content-length header // putHeaders(response,content,-1); String mimetype=(content==null?null:content.getContentTypeValue()); if (mimetype==null) LOG.warn("Unknown mimetype for "+request.getRequestURI()); MultiPartOutputStream multi = new MultiPartOutputStream(out); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); if (!response.containsHeader(HttpHeader.DATE.asString())) response.addDateHeader(HttpHeader.DATE.asString(),System.currentTimeMillis()); // If the request has a "Request-Range" header then we need to // send an old style multipart/x-byteranges Content-Type. This // keeps Netscape and acrobat happy. This is what Apache does. String ctp; if (request.getHeader(HttpHeader.REQUEST_RANGE.asString())!=null) ctp = "multipart/x-byteranges; boundary="; else ctp = "multipart/byteranges; boundary="; response.setContentType(ctp+multi.getBoundary()); InputStream in=content.getResource().getInputStream(); long pos=0; // calculate the content-length int length=0; String[] header = new String[ranges.size()]; for (int i=0;i0)?2:0)+ 2+multi.getBoundary().length()+2+ (mimetype==null?0:HttpHeader.CONTENT_TYPE.asString().length()+2+mimetype.length())+2+ HttpHeader.CONTENT_RANGE.asString().length()+2+header[i].length()+2+ 2+ (ibr.getLast(content_length)-ibr.getFirst(content_length))+1; } length+=2+2+multi.getBoundary().length()+2+2; response.setContentLength(length); for (int i=0;i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy