Search in sources :

Example 1 with ServletResponseWrapper

use of jakarta.servlet.ServletResponseWrapper 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();
    }
}
Also used : Response(org.apache.catalina.connector.Response) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ResponseFacade(org.apache.catalina.connector.ResponseFacade) ServletResponseWrapper(jakarta.servlet.ServletResponseWrapper)

Example 2 with ServletResponseWrapper

use of jakarta.servlet.ServletResponseWrapper in project tomcat by apache.

the class ApplicationDispatcher method wrapResponse.

/**
 * Create and return a response wrapper that has been inserted in the
 * appropriate spot in the response chain.
 */
private ServletResponse wrapResponse(State state) {
    // Locate the response we should insert in front of
    ServletResponse previous = null;
    ServletResponse current = state.outerResponse;
    while (current != null) {
        if (state.hresponse == null && (current instanceof HttpServletResponse)) {
            state.hresponse = (HttpServletResponse) current;
            if (!state.including) {
                // Forward only needs hresponse
                return null;
            }
        }
        if (!(current instanceof ServletResponseWrapper)) {
            break;
        }
        if (current instanceof ApplicationHttpResponse) {
            break;
        }
        if (current instanceof ApplicationResponse) {
            break;
        }
        previous = current;
        current = ((ServletResponseWrapper) current).getResponse();
    }
    // Instantiate a new wrapper at this point and insert it in the chain
    ServletResponse wrapper = null;
    if ((current instanceof ApplicationHttpResponse) || (current instanceof Response) || (current instanceof HttpServletResponse)) {
        wrapper = new ApplicationHttpResponse((HttpServletResponse) current, state.including);
    } else {
        wrapper = new ApplicationResponse(current, state.including);
    }
    if (previous == null) {
        state.outerResponse = wrapper;
    } else {
        ((ServletResponseWrapper) previous).setResponse(wrapper);
    }
    state.wrapResponse = wrapper;
    return wrapper;
}
Also used : Response(org.apache.catalina.connector.Response) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ServletResponseWrapper(jakarta.servlet.ServletResponseWrapper)

Example 3 with ServletResponseWrapper

use of jakarta.servlet.ServletResponseWrapper in project tomcat by apache.

the class ApplicationDispatcher method checkSameObjects.

private void checkSameObjects(ServletRequest appRequest, ServletResponse appResponse) throws ServletException {
    ServletRequest originalRequest = ApplicationFilterChain.getLastServicedRequest();
    ServletResponse originalResponse = ApplicationFilterChain.getLastServicedResponse();
    // Some forwards, eg from valves will not set original values
    if (originalRequest == null || originalResponse == null) {
        return;
    }
    boolean same = false;
    ServletRequest dispatchedRequest = appRequest;
    // find the request that was passed into the service method
    while (originalRequest instanceof ServletRequestWrapper && ((ServletRequestWrapper) originalRequest).getRequest() != null) {
        originalRequest = ((ServletRequestWrapper) originalRequest).getRequest();
    }
    // compare with the dispatched request
    while (!same) {
        if (originalRequest.equals(dispatchedRequest)) {
            same = true;
        }
        if (!same && dispatchedRequest instanceof ServletRequestWrapper) {
            dispatchedRequest = ((ServletRequestWrapper) dispatchedRequest).getRequest();
        } else {
            break;
        }
    }
    if (!same) {
        throw new ServletException(sm.getString("applicationDispatcher.specViolation.request"));
    }
    same = false;
    ServletResponse dispatchedResponse = appResponse;
    // find the response that was passed into the service method
    while (originalResponse instanceof ServletResponseWrapper && ((ServletResponseWrapper) originalResponse).getResponse() != null) {
        originalResponse = ((ServletResponseWrapper) originalResponse).getResponse();
    }
    // compare with the dispatched response
    while (!same) {
        if (originalResponse.equals(dispatchedResponse)) {
            same = true;
        }
        if (!same && dispatchedResponse instanceof ServletResponseWrapper) {
            dispatchedResponse = ((ServletResponseWrapper) dispatchedResponse).getResponse();
        } else {
            break;
        }
    }
    if (!same) {
        throw new ServletException(sm.getString("applicationDispatcher.specViolation.response"));
    }
}
Also used : ServletException(jakarta.servlet.ServletException) HttpServletRequest(jakarta.servlet.http.HttpServletRequest) ServletRequest(jakarta.servlet.ServletRequest) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ServletRequestWrapper(jakarta.servlet.ServletRequestWrapper) ServletResponseWrapper(jakarta.servlet.ServletResponseWrapper)

Example 4 with ServletResponseWrapper

use of jakarta.servlet.ServletResponseWrapper 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();
                }
            }
        }
    }
}
Also used : Ranges(org.apache.tomcat.util.http.parser.Ranges) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) ServletOutputStream(jakarta.servlet.ServletOutputStream) BufferedInputStream(java.io.BufferedInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) FileNotFoundException(java.io.FileNotFoundException) WebResource(org.apache.catalina.WebResource) Charset(java.nio.charset.Charset) BufferedInputStream(java.io.BufferedInputStream) ResponseFacade(org.apache.catalina.connector.ResponseFacade) CachedResource(org.apache.catalina.webresources.CachedResource) OutputStreamWriter(java.io.OutputStreamWriter) ServletResponseWrapper(jakarta.servlet.ServletResponseWrapper) PrintWriter(java.io.PrintWriter)

Aggregations

ServletResponse (jakarta.servlet.ServletResponse)4 ServletResponseWrapper (jakarta.servlet.ServletResponseWrapper)4 HttpServletResponse (jakarta.servlet.http.HttpServletResponse)4 Response (org.apache.catalina.connector.Response)2 ResponseFacade (org.apache.catalina.connector.ResponseFacade)2 ServletException (jakarta.servlet.ServletException)1 ServletOutputStream (jakarta.servlet.ServletOutputStream)1 ServletRequest (jakarta.servlet.ServletRequest)1 ServletRequestWrapper (jakarta.servlet.ServletRequestWrapper)1 HttpServletRequest (jakarta.servlet.http.HttpServletRequest)1 BufferedInputStream (java.io.BufferedInputStream)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 FileInputStream (java.io.FileInputStream)1 FileNotFoundException (java.io.FileNotFoundException)1 InputStream (java.io.InputStream)1 OutputStreamWriter (java.io.OutputStreamWriter)1 PrintWriter (java.io.PrintWriter)1 Charset (java.nio.charset.Charset)1 WebResource (org.apache.catalina.WebResource)1 CachedResource (org.apache.catalina.webresources.CachedResource)1