Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java')
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java775
1 files changed, 65 insertions, 710 deletions
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
index ab958d3fb8..5e2ac37ffc 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
@@ -18,23 +18,12 @@
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;
@@ -42,35 +31,19 @@ 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.GzipHttpContent;
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.ResourceHttpContent;
-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.http.pathmap.MappedResource;
import org.eclipse.jetty.server.ResourceCache;
import org.eclipse.jetty.server.ResourceContentFactory;
-import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.ResourceService;
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.QuotedStringTokenizer;
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;
@@ -148,36 +121,64 @@ 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 static final long serialVersionUID = 4930458713846881193L;
+
+ private final ResourceService _resourceService;
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<String> _gzipEquivalentFileExtensions;
+ public DefaultServlet()
+ {
+ _resourceService = new ResourceService()
+ {
+ @Override
+ protected String getWelcomeFile(String pathInContext)
+ {
+ 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)
+ {
+ MappedResource<ServletHolder> entry=_servletHandler.getHolderEntry(welcome_in_context);
+ if (entry!=null && entry.getResource()!=_defaultHolder &&
+ (_welcomeServlets || (_welcomeExactServlets && entry.getPathSpec().getDeclaration().equals(welcome_in_context))))
+ welcome_servlet=welcome_in_context;
+
+ }
+ }
+ return welcome_servlet;
+ }
+
+ @Override
+ protected void notFound(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ };
+ }
+
/* ------------------------------------------------------------ */
@Override
public void init()
@@ -192,12 +193,13 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
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);
-
+ _resourceService.setAcceptRanges(getInitBoolean("acceptRanges",_resourceService.isAcceptRanges()));
+ _resourceService.setDirAllowed(getInitBoolean("dirAllowed",_resourceService.isDirAllowed()));
+ _resourceService.setRedirectWelcome(getInitBoolean("redirectWelcome",_resourceService.isRedirectWelcome()));
+ _resourceService.setGzip(getInitBoolean("gzip",_resourceService.isGzip()));
+ _resourceService.setPathInfoOnly(getInitBoolean("pathInfoOnly",_resourceService.isPathInfoOnly()));
+ _resourceService.setEtags(getInitBoolean("etags",_resourceService.isEtags()));
+
if ("exact".equals(getInitParameter("welcomeServlets")))
{
_welcomeExactServlets=true;
@@ -248,8 +250,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String cc=getInitParameter("cacheControl");
if (cc!=null)
- _cacheControl=new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cc);
-
+ _resourceService.setCacheControl(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);
@@ -261,18 +264,13 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
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);
+ _cache = new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_resourceService.isEtags(),_resourceService.isGzip());
if (max_cache_size>=0)
_cache.setMaxCacheSize(max_cache_size);
if (max_cached_file_size>=-1)
@@ -288,16 +286,16 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
throw new UnavailableException(e.toString());
}
- if (_cache!=null)
- _contentFactory=_cache;
- else
+ HttpContent.Factory contentFactory=_cache;
+ if (contentFactory==null)
{
- _contentFactory=new ResourceContentFactory(this,_mimeTypes,_gzip);
+ contentFactory=new ResourceContentFactory(this,_mimeTypes,_resourceService.isGzip());
if (resourceCache!=null)
- _servletContext.setAttribute(resourceCache,_contentFactory);
+ _servletContext.setAttribute(resourceCache,contentFactory);
}
+ _resourceService.setContentFactory(contentFactory);
- _gzipEquivalentFileExtensions = new ArrayList<String>();
+ List<String> gzip_equivalent_file_extensions = new ArrayList<String>();
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
if (otherGzipExtensions != null)
{
@@ -306,15 +304,17 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
while (tok.hasMoreTokens())
{
String s = tok.nextToken().trim();
- _gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
+ gzip_equivalent_file_extensions.add((s.charAt(0)=='.'?s:"."+s));
}
}
else
{
//.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
- _gzipEquivalentFileExtensions.add(".svgz");
+ gzip_equivalent_file_extensions.add(".svgz");
}
+ _resourceService.setGzipEquivalentFileExtensions(gzip_equivalent_file_extensions);
+
_servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
for (ServletHolder h :_servletHandler.getServlets())
if (h.getServletInstance()==this)
@@ -433,196 +433,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
- String servletPath=null;
- String pathInfo=null;
- Enumeration<String> 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<String> reqRanges)
- {
- return (reqRanges!=null && reqRanges.hasMoreElements());
+ _resourceService.doGet(request,response);
}
/* ------------------------------------------------------------ */
@@ -651,462 +462,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
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 <code>Resource</code>.
- * 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 <code>null</code> is returned.
- * The list of welcome files is read from the {@link ContextHandler} for this servlet, or
- * <code>"index.jsp" , "index.html"</code> if that is <code>null</code>.
- * @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)
- {
- QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
- while (!match && quoted.hasMoreTokens())
- {
- String tag = quoted.nextToken();
- if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag)))
- match=true;
- }
- }
-
- 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
- QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifnm,", ",false,true);
- while (quoted.hasMoreTokens())
- {
- String tag = quoted.nextToken();
- 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<String> 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<InclusiveByteRange> 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;i<ranges.size();i++)
- {
- InclusiveByteRange ibr = ranges.get(i);
- header[i]=ibr.toHeaderRangeString(content_length);
- length+=
- ((i>0)?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<ranges.size();i++)
- {
- InclusiveByteRange ibr = ranges.get(i);
- multi.startPart(mimetype,new String[]{HttpHeader.CONTENT_RANGE+": "+header[i]});
-
- long start=ibr.getFirst(content_length);
- long size=ibr.getSize(content_length);
- if (in!=null)
- {
- // Handle non cached resource
- if (start<pos)
- {
- in.close();
- in=content.getResource().getInputStream();
- pos=0;
- }
- if (pos<start)
- {
- in.skip(start-pos);
- pos=start;
- }
-
- IO.copy(in,multi,size);
- pos+=size;
- }
- else
- // Handle cached resource
- content.getResource().writeTo(multi,start,size);
- }
- if (in!=null)
- in.close();
- multi.close();
- }
- return true;
- }
-
- /* ------------------------------------------------------------ */
- protected void putHeaders(HttpServletResponse response,HttpContent content, long contentLength)
- {
- if (response instanceof Response)
- {
- Response r = (Response)response;
- r.putHeaders(content,contentLength,_etags);
- HttpFields f = r.getHttpFields();
- if (_acceptRanges)
- f.put(ACCEPT_RANGES);
-
- if (_cacheControl!=null)
- f.put(_cacheControl);
- }
- else
- {
- Response.putHeaders(response,content,contentLength,_etags);
- if (_acceptRanges)
- response.setHeader(ACCEPT_RANGES.getName(),ACCEPT_RANGES.getValue());
-
- if (_cacheControl!=null)
- response.setHeader(_cacheControl.getName(),_cacheControl.getValue());
- }
- }
/* ------------------------------------------------------------ */
/*

Back to the top