转载 :http://blog.csdn.net/zdhcumt/article/details/6867264
附上部分源码:
package org.apache.catalina.servlets;
import ....
/**
* <p>The default resource-serving servlet for most web applications,
* used to serve static resources such as HTML pages and images.
* This servlet is intended to be mapped to <em>/</em> e.g.:
* </p>
* <pre>
* <servlet-mapping>
* <servlet-name>default</servlet-name>
* <url-pattern>/</url-pattern>
* </servlet-mapping>
* </pre>
* <p>It can be mapped to sub-paths, however in all cases resources are served
* from the web appplication resource root using the full path from the root
* of the web application context.
* <br/>e.g. given a web application structure:
*</p>
* <pre>
* /context
* /images
* tomcat2.jpg
* /static
* /images
* tomcat.jpg
* </pre>
* <p>
* ... and a servlet mapping that maps only <code>/static/*</code> to the default servlet:
* </p>
* <pre>
* <servlet-mapping>
* <servlet-name>default</servlet-name>
* <url-pattern>/static/*</url-pattern>
* </servlet-mapping>
* </pre>
* <p>
* Then a request to <code>/context/static/images/tomcat.jpg</code> will succeed
* while a request to <code>/context/images/tomcat2.jpg</code> will fail.
* </p>
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Id: DefaultServlet.java 1086995 2011-03-30 15:50:28Z markt $
*/
public class DefaultServlet extends HttpServlet {
/**
* The output buffer size to use when serving resources.
*/
protected int output = 2048;
/**
* Size of file transfer buffer in bytes.
*/
protected static final int BUFFER_SIZE = 4096;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, including the data content
serveResource(request, response, true);
}
/**
* Serve the specified resource, optionally including the data content.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param content Should the content be included?
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void serveResource(HttpServletRequest request,
HttpServletResponse response,
boolean content)
throws IOException, ServletException {
boolean serveContent = content;
// Identify the requested resource path
String path = getRelativePath(request);
if (debug > 0) {
if (serveContent)
log("DefaultServlet.serveResource: Serving resource '" +
path + "' headers and data");
else
log("DefaultServlet.serveResource: Serving resource '" +
path + "' headers only");
}
CacheEntry cacheEntry = resources.lookupCache(path);
if (!cacheEntry.exists) {
// Check if we're included so we can return the appropriate
// missing resource name in the error
String requestUri = (String) request.getAttribute(
RequestDispatcher.INCLUDE_REQUEST_URI);
if (requestUri == null) {
requestUri = request.getRequestURI();
} else {
// We're included
// SRV.9.3 says we must throw a FNFE
throw new FileNotFoundException(
sm.getString("defaultServlet.missingResource",
requestUri));
}
response.sendError(HttpServletResponse.SC_NOT_FOUND,
requestUri);
return;
}
// If the resource is not a collection, and the resource path
// ends with "/" or "\", return NOT FOUND
if (cacheEntry.context == null) {
if (path.endsWith("/") || (path.endsWith("\\"))) {
// Check if we're included so we can return the appropriate
// missing resource name in the error
String requestUri = (String) request.getAttribute(
RequestDispatcher.INCLUDE_REQUEST_URI);
if (requestUri == null) {
requestUri = request.getRequestURI();
}
response.sendError(HttpServletResponse.SC_NOT_FOUND,
requestUri);
return;
}
}
boolean isError =
response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST;
// Check if the conditions specified in the optional If headers are
// satisfied.
if (cacheEntry.context == null) {
// Checking If headers
boolean included = (request.getAttribute(
RequestDispatcher.INCLUDE_CONTEXT_PATH) != null);
if (!included && !isError &&
!checkIfHeaders(request, response, cacheEntry.attributes)) {
return;
}
}
// Find content type.
String contentType = cacheEntry.attributes.getMimeType();
if (contentType == null) {
contentType = getServletContext().getMimeType(cacheEntry.name);
cacheEntry.attributes.setMimeType(contentType);
}
ArrayList<Range> ranges = null;
long contentLength = -1L;
if (cacheEntry.context != null) {
// Skip directory listings if we have been configured to
// suppress them
if (!listings) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
request.getRequestURI());
return;
}
contentType = "text/html;charset=UTF-8";
} else {
if (!isError) {
if (useAcceptRanges) {
// Accept ranges header
response.setHeader("Accept-Ranges", "bytes");
}
// Parse range specifier
ranges = parseRange(request, response, cacheEntry.attributes);
// ETag header
response.setHeader("ETag", cacheEntry.attributes.getETag());
// Last-Modified header
response.setHeader("Last-Modified",
cacheEntry.attributes.getLastModifiedHttp());
}
// Get content length
contentLength = cacheEntry.attributes.getContentLength();
// Special case for zero length files, which would cause a
// (silent) ISE when setting the output buffer size
if (contentLength == 0L) {
serveContent = false;
}
}
ServletOutputStream ostream = null;
PrintWriter writer = null;
if (serveContent) {
// Trying to retrieve the servlet output stream
try {
ostream = response.getOutputStream();
} catch (IllegalStateException e) {
// If it fails, we try to get a Writer instead if we're
// trying to serve a text file
if ( (contentType == null)
|| (contentType.startsWith("text"))
|| (contentType.endsWith("xml")) ) {
writer = response.getWriter();
// Cannot reliably serve partial content with a Writer
ranges = FULL;
} else {
throw e;
}
}
}
// Check to see if a Filter, Valve of wrapper has written some content.
// If it has, disable range requests and setting of a content length
// since neither can be done reliably.
ServletResponse r = response;
long contentWritten = 0;
while (r instanceof ServletResponseWrapper) {
r = ((ServletResponseWrapper) r).getResponse();
}
if (r instanceof ResponseFacade) {
contentWritten = ((ResponseFacade) r).getContentWritten();
}
if (contentWritten > 0) {
ranges = FULL;
}
if ( (cacheEntry.context != null)
|| isError
|| ( ((ranges == null) || (ranges.isEmpty()))
&& (request.getHeader("Range") == null) )
|| (ranges == FULL) ) {
// Set the appropriate output headers
if (contentType != null) {
if (debug > 0)
log("DefaultServlet.serveFile: contentType='" +
contentType + "'");
response.setContentType(contentType);
}
if ((cacheEntry.resource != null) && (contentLength >= 0)
&& (!serveContent || ostream != null)) {
if (debug > 0)
log("DefaultServlet.serveFile: contentLength=" +
contentLength);
// Don't set a content length if something else has already
// written to the response.
if (contentWritten == 0) {
if (contentLength < Integer.MAX_VALUE) {
response.setContentLength((int) contentLength);
} else {
// Set the content-length as String to be able to use a
// long
response.setHeader("content-length",
"" + contentLength);
}
}
}
InputStream renderResult = null;
if (cacheEntry.context != null) {
if (serveContent) {
// Serve the directory browser
renderResult = render(getPathPrefix(request), cacheEntry);
}
}
// Copy the input stream to our output stream (if requested)
if (serveContent) {
try {
response.setBufferSize(output);
} catch (IllegalStateException e) {
// Silent catch
}
if (ostream != null) {
if (!checkSendfile(request, response, cacheEntry, contentLength, null))
copy(cacheEntry, renderResult, ostream);
} else {
copy(cacheEntry, renderResult, writer);
}
}
} else {
if ((ranges == null) || (ranges.isEmpty()))
return;
// Partial content response.
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (ranges.size() == 1) {
Range range = ranges.get(0);
response.addHeader("Content-Range", "bytes "
+ range.start
+ "-" + range.end + "/"
+ range.length);
long length = range.end - range.start + 1;
if (length < Integer.MAX_VALUE) {
response.setContentLength((int) length);
} else {
// Set the content-length as String to be able to use a long
response.setHeader("content-length", "" + length);
}
if (contentType != null) {
if (debug > 0)
log("DefaultServlet.serveFile: contentType='" +
contentType + "'");
response.setContentType(contentType);
}
if (serveContent) {
try {
response.setBufferSize(output);
} catch (IllegalStateException e) {
// Silent catch
}
if (ostream != null) {
if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
copy(cacheEntry, ostream, range);
} else {
// we should not get here
throw new IllegalStateException();
}
}
} else {
response.setContentType("multipart/byteranges; boundary="
+ mimeSeparation);
if (serveContent) {
try {
response.setBufferSize(output);
} catch (IllegalStateException e) {
// Silent catch
}
if (ostream != null) {
copy(cacheEntry, ostream, ranges.iterator(),
contentType);
} else {
// we should not get here
throw new IllegalStateException();
}
}
}
}
}
/**
* Parse the content-range header.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @return Range
*/
protected Range parseContentRange(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// Retrieving the content-range header (if any is specified
String rangeHeader = request.getHeader("Content-Range");
if (rangeHeader == null)
return null;
// bytes is the only range unit supported
if (!rangeHeader.startsWith("bytes")) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return null;
}
rangeHeader = rangeHeader.substring(6).trim();
int dashPos = rangeHeader.indexOf('-');
int slashPos = rangeHeader.indexOf('/');
if (dashPos == -1) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return null;
}
if (slashPos == -1) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return null;
}
Range range = new Range();
try {
range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
range.end =
Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
range.length = Long.parseLong
(rangeHeader.substring(slashPos + 1, rangeHeader.length()));
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return null;
}
if (!range.validate()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return null;
}
return range;
}
/**
* Parse the range header.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @return Vector of ranges
*/
protected ArrayList<Range> parseRange(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes) throws IOException {
// Checking If-Range
String headerValue = request.getHeader("If-Range");
if (headerValue != null) {
long headerValueTime = (-1L);
try {
headerValueTime = request.getDateHeader("If-Range");
} catch (IllegalArgumentException e) {
// Ignore
}
String eTag = resourceAttributes.getETag();
long lastModified = resourceAttributes.getLastModified();
if (headerValueTime == (-1L)) {
// If the ETag the client gave does not match the entity
// etag, then the entire entity is returned.
if (!eTag.equals(headerValue.trim()))
return FULL;
} else {
// If the timestamp of the entity the client got is older than
// the last modification date of the entity, the entire entity
// is returned.
if (lastModified > (headerValueTime + 1000))
return FULL;
}
}
long fileLength = resourceAttributes.getContentLength();
if (fileLength == 0)
return null;
// Retrieving the range header (if any is specified
String rangeHeader = request.getHeader("Range");
if (rangeHeader == null)
return null;
// bytes is the only range unit supported (and I don't see the point
// of adding new ones).
if (!rangeHeader.startsWith("bytes")) {
response.addHeader("Content-Range", "bytes */" + fileLength);
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
rangeHeader = rangeHeader.substring(6);
// Vector which will contain all the ranges which are successfully
// parsed.
ArrayList<Range> result = new ArrayList<Range>();
StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
// Parsing the range list
while (commaTokenizer.hasMoreTokens()) {
String rangeDefinition = commaTokenizer.nextToken().trim();
Range currentRange = new Range();
currentRange.length = fileLength;
int dashPos = rangeDefinition.indexOf('-');
if (dashPos == -1) {
response.addHeader("Content-Range", "bytes */" + fileLength);
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
if (dashPos == 0) {
try {
long offset = Long.parseLong(rangeDefinition);
currentRange.start = fileLength + offset;
currentRange.end = fileLength - 1;
} catch (NumberFormatException e) {
response.addHeader("Content-Range",
"bytes */" + fileLength);
response.sendError
(HttpServletResponse
.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
} else {
try {
currentRange.start = Long.parseLong
(rangeDefinition.substring(0, dashPos));
if (dashPos < rangeDefinition.length() - 1)
currentRange.end = Long.parseLong
(rangeDefinition.substring
(dashPos + 1, rangeDefinition.length()));
else
currentRange.end = fileLength - 1;
} catch (NumberFormatException e) {
response.addHeader("Content-Range",
"bytes */" + fileLength);
response.sendError
(HttpServletResponse
.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
}
if (!currentRange.validate()) {
response.addHeader("Content-Range", "bytes */" + fileLength);
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
result.add(currentRange);
}
return result;
}
protected static class Range {
public long start;
public long end;
public long length;
/**
* Validate range.
*/
public boolean validate() {
if (end >= length)
end = length - 1;
return (start >= 0) && (end >= 0) && (start <= end) && (length > 0);
}
}
}