Search in sources :

Example 11 with ServletResponse

use of jakarta.servlet.ServletResponse 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 12 with ServletResponse

use of jakarta.servlet.ServletResponse 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 13 with ServletResponse

use of jakarta.servlet.ServletResponse 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 14 with ServletResponse

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

the class CsrfPreventionFilter method doFilter.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    ServletResponse wResponse = null;
    if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        boolean skipNonceCheck = false;
        if (Constants.METHOD_GET.equals(req.getMethod()) && entryPoints.contains(getRequestedPath(req))) {
            if (log.isTraceEnabled()) {
                log.trace("Skipping CSRF nonce-check for GET request to entry point " + getRequestedPath(req));
            }
            skipNonceCheck = true;
        }
        HttpSession session = req.getSession(false);
        @SuppressWarnings("unchecked") LruCache<String> nonceCache = (session == null) ? null : (LruCache<String>) session.getAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME);
        if (!skipNonceCheck) {
            String previousNonce = req.getParameter(nonceRequestParameterName);
            if (previousNonce == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Rejecting request for " + getRequestedPath(req) + ", session " + (null == session ? "(none)" : session.getId()) + " with no CSRF nonce found in request");
                }
                res.sendError(getDenyStatus());
                return;
            } else if (nonceCache == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Rejecting request for " + getRequestedPath(req) + ", session " + (null == session ? "(none)" : session.getId()) + " due to empty / missing nonce cache");
                }
                res.sendError(getDenyStatus());
                return;
            } else if (!nonceCache.contains(previousNonce)) {
                if (log.isDebugEnabled()) {
                    log.debug("Rejecting request for " + getRequestedPath(req) + ", session " + (null == session ? "(none)" : session.getId()) + " due to invalid nonce " + previousNonce);
                }
                res.sendError(getDenyStatus());
                return;
            }
            if (log.isTraceEnabled()) {
                log.trace("Allowing request to " + getRequestedPath(req) + " with valid CSRF nonce " + previousNonce);
            }
        }
        if (nonceCache == null) {
            if (log.isDebugEnabled()) {
                log.debug("Creating new CSRF nonce cache with size=" + nonceCacheSize + " for session " + (null == session ? "(will create)" : session.getId()));
            }
            nonceCache = new LruCache<>(nonceCacheSize);
            if (session == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Creating new session to store CSRF nonce cache");
                }
                session = req.getSession(true);
            }
            session.setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
        }
        String newNonce = generateNonce();
        nonceCache.add(newNonce);
        // Take this request's nonce and put it into the request
        // attributes so pages can make direct use of it, rather than
        // requiring the use of response.encodeURL.
        request.setAttribute(Constants.CSRF_NONCE_REQUEST_ATTR_NAME, newNonce);
        wResponse = new CsrfResponseWrapper(res, nonceRequestParameterName, newNonce);
    } else {
        wResponse = response;
    }
    chain.doFilter(request, wResponse);
}
Also used : HttpServletRequest(jakarta.servlet.http.HttpServletRequest) ServletResponse(jakarta.servlet.ServletResponse) HttpServletResponse(jakarta.servlet.http.HttpServletResponse) HttpSession(jakarta.servlet.http.HttpSession) HttpServletResponse(jakarta.servlet.http.HttpServletResponse)

Example 15 with ServletResponse

use of jakarta.servlet.ServletResponse 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)21 HttpServletResponse (jakarta.servlet.http.HttpServletResponse)17 ServletRequest (jakarta.servlet.ServletRequest)13 HttpServletRequest (jakarta.servlet.http.HttpServletRequest)10 ServletException (jakarta.servlet.ServletException)9 IOException (java.io.IOException)8 Test (org.junit.jupiter.api.Test)5 ServletResponseWrapper (jakarta.servlet.ServletResponseWrapper)4 MockFilterChain (org.springframework.mock.web.MockFilterChain)4 MockHttpServletRequest (org.springframework.mock.web.MockHttpServletRequest)4 MockHttpServletResponse (org.springframework.mock.web.MockHttpServletResponse)4 MockHttpServletRequest (org.springframework.web.testfixture.servlet.MockHttpServletRequest)4 MockHttpServletResponse (org.springframework.web.testfixture.servlet.MockHttpServletResponse)4 Response (org.apache.catalina.connector.Response)3 AsyncEvent (jakarta.servlet.AsyncEvent)2 ServletOutputStream (jakarta.servlet.ServletOutputStream)2 HashMap (java.util.HashMap)2 Properties (java.util.Properties)2 Context (org.apache.catalina.Context)2 ResponseFacade (org.apache.catalina.connector.ResponseFacade)2