use of org.apache.catalina.connector.ResponseFacade in project tomcat by apache.
the class ApplicationDispatcher method unwrapResponse.
/**
* Unwrap the response if we have wrapped it.
*/
private void unwrapResponse(State state) {
if (state.wrapResponse == null) {
return;
}
if (state.outerRequest.isAsyncStarted()) {
if (!state.outerRequest.getAsyncContext().hasOriginalRequestAndResponse()) {
return;
}
}
ServletResponse previous = null;
ServletResponse current = state.outerResponse;
while (current != null) {
// If we run into the container response we are done
if ((current instanceof Response) || (current instanceof ResponseFacade)) {
break;
}
// Remove the current response if it is our wrapper
if (current == state.wrapResponse) {
ServletResponse next = ((ServletResponseWrapper) current).getResponse();
if (previous == null) {
state.outerResponse = next;
} else {
((ServletResponseWrapper) previous).setResponse(next);
}
break;
}
// Advance to the next response in the chain
previous = current;
current = ((ServletResponseWrapper) current).getResponse();
}
}
use of org.apache.catalina.connector.ResponseFacade in project tomcat by apache.
the class ApplicationDispatcher method doForward.
private void doForward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// Reset any output that has been buffered, but keep headers/cookies
if (response.isCommitted()) {
throw new IllegalStateException(sm.getString("applicationDispatcher.forward.ise"));
}
try {
response.resetBuffer();
} catch (IllegalStateException e) {
throw e;
}
// Set up to handle the specified request and response
State state = new State(request, response, false);
if (context.getDispatcherWrapsSameObject()) {
// Check SRV.9.2 / RequestDispacther Javadoc
checkSameObjects(request, response);
}
wrapResponse(state);
// Handle an HTTP named dispatcher forward
if ((servletPath == null) && (pathInfo == null)) {
ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state);
HttpServletRequest hrequest = state.hrequest;
wrequest.setRequestURI(hrequest.getRequestURI());
wrequest.setContextPath(hrequest.getContextPath());
wrequest.setServletPath(hrequest.getServletPath());
wrequest.setPathInfo(hrequest.getPathInfo());
wrequest.setQueryString(hrequest.getQueryString());
processRequest(request, response, state);
} else // Handle an HTTP path-based forward
{
ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state);
HttpServletRequest hrequest = state.hrequest;
if (hrequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI) == null) {
wrequest.setAttribute(RequestDispatcher.FORWARD_REQUEST_URI, hrequest.getRequestURI());
wrequest.setAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH, hrequest.getContextPath());
wrequest.setAttribute(RequestDispatcher.FORWARD_SERVLET_PATH, hrequest.getServletPath());
wrequest.setAttribute(RequestDispatcher.FORWARD_PATH_INFO, hrequest.getPathInfo());
wrequest.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING, hrequest.getQueryString());
wrequest.setAttribute(RequestDispatcher.FORWARD_MAPPING, hrequest.getHttpServletMapping());
}
wrequest.setContextPath(context.getEncodedPath());
wrequest.setRequestURI(requestURI);
wrequest.setServletPath(servletPath);
wrequest.setPathInfo(pathInfo);
if (queryString != null) {
wrequest.setQueryString(queryString);
wrequest.setQueryParams(queryString);
}
wrequest.setMapping(mapping);
processRequest(request, response, state);
}
if (request.isAsyncStarted()) {
// response as it may be written to during the async handling
return;
}
// This is not a real close in order to support error processing
if (wrapper.getLogger().isDebugEnabled()) {
wrapper.getLogger().debug(" Disabling the response for further output");
}
if (response instanceof ResponseFacade) {
((ResponseFacade) response).finish();
} else {
// and may no longer be instance of RequestFacade
if (wrapper.getLogger().isDebugEnabled()) {
wrapper.getLogger().debug(" The Response is vehiculed using a wrapper: " + response.getClass().getName());
}
// Close anyway
try {
PrintWriter writer = response.getWriter();
writer.close();
} catch (IllegalStateException e) {
try {
ServletOutputStream stream = response.getOutputStream();
stream.close();
} catch (IllegalStateException | IOException f) {
// Ignore
}
} catch (IOException e) {
// Ignore
}
}
}
use of org.apache.catalina.connector.ResponseFacade in project tomcat by apache.
the class DefaultServlet method serveResource.
/**
* 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?
* @param inputEncoding The encoding to use if it is necessary to access the
* source as characters rather than as bytes
*
* @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, String inputEncoding) throws IOException, ServletException {
boolean serveContent = content;
// Identify the requested resource path
String path = getRelativePath(request, true);
if (debug > 0) {
if (serveContent) {
log("DefaultServlet.serveResource: Serving resource '" + path + "' headers and data");
} else {
log("DefaultServlet.serveResource: Serving resource '" + path + "' headers only");
}
}
if (path.length() == 0) {
// Context root redirect
doDirectoryRedirect(request, response);
return;
}
WebResource resource = resources.getResource(path);
boolean isError = DispatcherType.ERROR == request.getDispatcherType();
if (!resource.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 {
// SRV.9.3 says we must throw a FNFE
throw new FileNotFoundException(sm.getString("defaultServlet.missingResource", requestUri));
}
if (isError) {
response.sendError(((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue());
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("defaultServlet.missingResource", requestUri));
}
return;
}
if (!resource.canRead()) {
// 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 {
// reasonable
throw new FileNotFoundException(sm.getString("defaultServlet.missingResource", requestUri));
}
if (isError) {
response.sendError(((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue());
} else {
response.sendError(HttpServletResponse.SC_FORBIDDEN, requestUri);
}
return;
}
boolean included = false;
// satisfied.
if (resource.isFile()) {
// Checking If headers
included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null);
if (!included && !isError && !checkIfHeaders(request, response, resource)) {
return;
}
}
// Find content type.
String contentType = resource.getMimeType();
if (contentType == null) {
contentType = getServletContext().getMimeType(resource.getName());
resource.setMimeType(contentType);
}
// These need to reflect the original resource, not the potentially
// precompressed version of the resource so get them now if they are going to
// be needed later
String eTag = null;
String lastModifiedHttp = null;
if (resource.isFile() && !isError) {
eTag = generateETag(resource);
lastModifiedHttp = resource.getLastModifiedHttp();
}
// Serve a precompressed version of the file if present
boolean usingPrecompressedVersion = false;
if (compressionFormats.length > 0 && !included && resource.isFile() && !pathEndsWithCompressedExtension(path)) {
List<PrecompressedResource> precompressedResources = getAvailablePrecompressedResources(path);
if (!precompressedResources.isEmpty()) {
ResponseUtil.addVaryFieldName(response, "accept-encoding");
PrecompressedResource bestResource = getBestPrecompressedResource(request, precompressedResources);
if (bestResource != null) {
response.addHeader("Content-Encoding", bestResource.format.encoding);
resource = bestResource.resource;
usingPrecompressedVersion = true;
}
}
}
Ranges ranges = FULL;
long contentLength = -1L;
if (resource.isDirectory()) {
if (!path.endsWith("/")) {
doDirectoryRedirect(request, response);
return;
}
// suppress them
if (!listings) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("defaultServlet.missingResource", 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, resource);
if (ranges == null) {
return;
}
// ETag header
response.setHeader("ETag", eTag);
// Last-Modified header
response.setHeader("Last-Modified", lastModifiedHttp);
}
// Get content length
contentLength = resource.getContentLength();
// (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) {
// trying to serve a text file
if (!usingPrecompressedVersion && isText(contentType)) {
writer = response.getWriter();
// Cannot reliably serve partial content with a Writer
ranges = FULL;
} else {
throw e;
}
}
}
// Check to see if a Filter, Valve or 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;
}
String outputEncoding = response.getCharacterEncoding();
Charset charset = B2CConverter.getCharset(outputEncoding);
boolean conversionRequired;
/*
* The test below deliberately uses != to compare two Strings. This is
* because the code is looking to see if the default character encoding
* has been returned because no explicit character encoding has been
* defined. There is no clean way of doing this via the Servlet API. It
* would be possible to add a Tomcat specific API but that would require
* quite a bit of code to get to the Tomcat specific request object that
* may have been wrapped. The != test is a (slightly hacky) quick way of
* doing this.
*/
boolean outputEncodingSpecified = outputEncoding != org.apache.coyote.Constants.DEFAULT_BODY_CHARSET.name() && outputEncoding != resources.getContext().getResponseCharacterEncoding();
if (!usingPrecompressedVersion && isText(contentType) && outputEncodingSpecified && !charset.equals(fileEncodingCharset)) {
conversionRequired = true;
// Conversion often results fewer/more/different bytes.
// That does not play nicely with range requests.
ranges = FULL;
} else {
conversionRequired = false;
}
if (resource.isDirectory() || isError || ranges == FULL) {
// Set the appropriate output headers
if (contentType != null) {
if (debug > 0) {
log("DefaultServlet.serveFile: contentType='" + contentType + "'");
}
// Don't override a previously set content type
if (response.getContentType() == null) {
response.setContentType(contentType);
}
}
if (resource.isFile() && contentLength >= 0 && (!serveContent || ostream != null)) {
if (debug > 0) {
log("DefaultServlet.serveFile: contentLength=" + contentLength);
}
// written to the response or if conversion will be taking place
if (contentWritten == 0 && !conversionRequired) {
response.setContentLengthLong(contentLength);
}
}
if (serveContent) {
try {
response.setBufferSize(output);
} catch (IllegalStateException e) {
// Silent catch
}
InputStream renderResult = null;
if (ostream == null) {
// content directly.
if (resource.isDirectory()) {
renderResult = render(request, getPathPrefix(request), resource, inputEncoding);
} else {
renderResult = resource.getInputStream();
if (included) {
// Need to make sure any BOM is removed
if (!renderResult.markSupported()) {
renderResult = new BufferedInputStream(renderResult);
}
Charset bomCharset = processBom(renderResult, useBomIfPresent.stripBom);
if (bomCharset != null && useBomIfPresent.useBomEncoding) {
inputEncoding = bomCharset.name();
}
}
}
copy(renderResult, writer, inputEncoding);
} else {
// Output is via an OutputStream
if (resource.isDirectory()) {
renderResult = render(request, getPathPrefix(request), resource, inputEncoding);
} else {
// Check to see if conversion is required
if (conversionRequired || included) {
// When including a file, we need to check for a BOM
// to determine if a conversion is required, so we
// might as well always convert
InputStream source = resource.getInputStream();
if (!source.markSupported()) {
source = new BufferedInputStream(source);
}
Charset bomCharset = processBom(source, useBomIfPresent.stripBom);
if (bomCharset != null && useBomIfPresent.useBomEncoding) {
inputEncoding = bomCharset.name();
}
// specified
if (outputEncodingSpecified) {
OutputStreamWriter osw = new OutputStreamWriter(ostream, charset);
PrintWriter pw = new PrintWriter(osw);
copy(source, pw, inputEncoding);
pw.flush();
} else {
// Just included but no conversion
renderResult = source;
}
} else {
if (!checkSendfile(request, response, resource, contentLength, null)) {
// sendfile not possible so check if resource
// content is available directly via
// CachedResource. Do not want to call
// getContent() on other resource
// implementations as that could trigger loading
// the contents of a very large file into memory
byte[] resourceBody = null;
if (resource instanceof CachedResource) {
resourceBody = resource.getContent();
}
if (resourceBody == null) {
// Resource content not directly available,
// use InputStream
renderResult = resource.getInputStream();
} else {
// Use the resource content directly
ostream.write(resourceBody);
}
}
}
}
// the output (this method closes the stream)
if (renderResult != null) {
copy(renderResult, ostream);
}
}
}
} else {
if ((ranges == null) || (ranges.getEntries().isEmpty())) {
return;
}
// Partial content response.
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (ranges.getEntries().size() == 1) {
Ranges.Entry range = ranges.getEntries().get(0);
long start = getStart(range, contentLength);
long end = getEnd(range, contentLength);
response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
long length = end - start + 1;
response.setContentLengthLong(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, resource, contentLength, range)) {
copy(resource, contentLength, 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(resource, contentLength, ostream, ranges, contentType);
} else {
// we should not get here
throw new IllegalStateException();
}
}
}
}
}
Aggregations