Search in sources :

Example 16 with CharChunk

use of org.apache.tomcat.util.buf.CharChunk in project tomcat by apache.

the class CoyoteAdapter method postParseRequest.

// ------------------------------------------------------ Protected Methods
/**
 * Perform the necessary processing after the HTTP headers have been parsed
 * to enable the request/response pair to be passed to the start of the
 * container pipeline for processing.
 *
 * @param req      The coyote request object
 * @param request  The catalina request object
 * @param res      The coyote response object
 * @param response The catalina response object
 *
 * @return <code>true</code> if the request should be passed on to the start
 *         of the container pipeline, otherwise <code>false</code>
 *
 * @throws IOException If there is insufficient space in a buffer while
 *                     processing headers
 * @throws ServletException If the supported methods of the target servlet
 *                          cannot be determined
 */
protected boolean postParseRequest(org.apache.coyote.Request req, Request request, org.apache.coyote.Response res, Response response) throws IOException, ServletException {
    // processor hasn't set it, use the settings from the connector
    if (req.scheme().isNull()) {
        // Use connector scheme and secure configuration, (defaults to
        // "http" and false respectively)
        req.scheme().setString(connector.getScheme());
        request.setSecure(connector.getSecure());
    } else {
        // Use processor specified scheme to determine secure state
        request.setSecure(req.scheme().equals("https"));
    }
    // At this point the Host header has been processed.
    // Override if the proxyPort/proxyHost are set
    String proxyName = connector.getProxyName();
    int proxyPort = connector.getProxyPort();
    if (proxyPort != 0) {
        req.setServerPort(proxyPort);
    } else if (req.getServerPort() == -1) {
        // Not explicitly set. Use default ports based on the scheme
        if (req.scheme().equals("https")) {
            req.setServerPort(443);
        } else {
            req.setServerPort(80);
        }
    }
    if (proxyName != null) {
        req.serverName().setString(proxyName);
    }
    MessageBytes undecodedURI = req.requestURI();
    // Check for ping OPTIONS * request
    if (undecodedURI.equals("*")) {
        if (req.method().equalsIgnoreCase("OPTIONS")) {
            StringBuilder allow = new StringBuilder();
            allow.append("GET, HEAD, POST, PUT, DELETE, OPTIONS");
            // Trace if allowed
            if (connector.getAllowTrace()) {
                allow.append(", TRACE");
            }
            res.setHeader("Allow", allow.toString());
            // Access log entry as processing won't reach AccessLogValve
            connector.getService().getContainer().logAccess(request, response, 0, true);
            return false;
        } else {
            response.sendError(400, "Invalid URI");
        }
    }
    MessageBytes decodedURI = req.decodedURI();
    if (undecodedURI.getType() == MessageBytes.T_BYTES) {
        if (connector.getRejectSuspiciousURIs()) {
            if (checkSuspiciousURIs(undecodedURI.getByteChunk())) {
                response.sendError(400, "Invalid URI");
            }
        }
        // Copy the raw URI to the decodedURI
        decodedURI.duplicate(undecodedURI);
        // Parse (and strip out) the path parameters
        parsePathParameters(req, request);
        // %xx decoding of the URL
        try {
            req.getURLDecoder().convert(decodedURI.getByteChunk(), connector.getEncodedSolidusHandlingInternal());
        } catch (IOException ioe) {
            response.sendError(400, "Invalid URI: " + ioe.getMessage());
        }
        // Normalization
        if (normalize(req.decodedURI(), connector.getAllowBackslash())) {
            // Character decoding
            convertURI(decodedURI, request);
        // URIEncoding values are limited to US-ASCII supersets.
        // Therefore it is not necessary to check that the URI remains
        // normalized after character decoding
        } else {
            response.sendError(400, "Invalid URI");
        }
    } else {
        /* The URI is chars or String, and has been sent using an in-memory
             * protocol handler. The following assumptions are made:
             * - req.requestURI() has been set to the 'original' non-decoded,
             *   non-normalized URI
             * - req.decodedURI() has been set to the decoded, normalized form
             *   of req.requestURI()
             * - 'suspicious' URI filtering - if required - has already been
             *   performed
             */
        decodedURI.toChars();
        // Remove all path parameters; any needed path parameter should be set
        // using the request object rather than passing it in the URL
        CharChunk uriCC = decodedURI.getCharChunk();
        int semicolon = uriCC.indexOf(';');
        if (semicolon > 0) {
            decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(), semicolon);
        }
    }
    // Request mapping.
    MessageBytes serverName;
    if (connector.getUseIPVHosts()) {
        serverName = req.localName();
        if (serverName.isNull()) {
            // well, they did ask for it
            res.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, null);
        }
    } else {
        serverName = req.serverName();
    }
    // Version for the second mapping loop and
    // Context that we expect to get for that version
    String version = null;
    Context versionContext = null;
    boolean mapRequired = true;
    if (response.isError()) {
        // An error this early means the URI is invalid. Ensure invalid data
        // is not passed to the mapper. Note we still want the mapper to
        // find the correct host.
        decodedURI.recycle();
    }
    while (mapRequired) {
        // This will map the the latest version by default
        connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());
        // so no context could be mapped.
        if (request.getContext() == null) {
            // body.
            return true;
        }
        // Now we have the context, we can parse the session ID from the URL
        // (if any). Need to do this before we redirect in case we need to
        // include the session id in the redirect
        String sessionID;
        if (request.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.URL)) {
            // Get the session ID if there was one
            sessionID = request.getPathParameter(SessionConfig.getSessionUriParamName(request.getContext()));
            if (sessionID != null) {
                request.setRequestedSessionId(sessionID);
                request.setRequestedSessionURL(true);
            }
        }
        // Look for session ID in cookies and SSL session
        try {
            parseSessionCookiesId(request);
        } catch (IllegalArgumentException e) {
            // Too many cookies
            if (!response.isError()) {
                response.setError();
                response.sendError(400);
            }
            return true;
        }
        parseSessionSslId(request);
        sessionID = request.getRequestedSessionId();
        mapRequired = false;
        if (version != null && request.getContext() == versionContext) {
        // We got the version that we asked for. That is it.
        } else {
            version = null;
            versionContext = null;
            Context[] contexts = request.getMappingData().contexts;
            // No session ID means no possibility of remap
            if (contexts != null && sessionID != null) {
                // Find the context associated with the session
                for (int i = contexts.length; i > 0; i--) {
                    Context ctxt = contexts[i - 1];
                    if (ctxt.getManager().findSession(sessionID) != null) {
                        // already been mapped?
                        if (!ctxt.equals(request.getMappingData().context)) {
                            // Set version so second time through mapping
                            // the correct context is found
                            version = ctxt.getWebappVersion();
                            versionContext = ctxt;
                            // Reset mapping
                            request.getMappingData().recycle();
                            mapRequired = true;
                            // Recycle cookies and session info in case the
                            // correct context is configured with different
                            // settings
                            request.recycleSessionInfo();
                            request.recycleCookieInfo(true);
                        }
                        break;
                    }
                }
            }
        }
        if (!mapRequired && request.getContext().getPaused()) {
            // point.
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            // Should never happen
            }
            // Reset mapping
            request.getMappingData().recycle();
            mapRequired = true;
        }
    }
    // Possible redirect
    MessageBytes redirectPathMB = request.getMappingData().redirectPath;
    if (!redirectPathMB.isNull()) {
        String redirectPath = URLEncoder.DEFAULT.encode(redirectPathMB.toString(), StandardCharsets.UTF_8);
        String query = request.getQueryString();
        if (request.isRequestedSessionIdFromURL()) {
            // This is not optimal, but as this is not very common, it
            // shouldn't matter
            redirectPath = redirectPath + ";" + SessionConfig.getSessionUriParamName(request.getContext()) + "=" + request.getRequestedSessionId();
        }
        if (query != null) {
            // This is not optimal, but as this is not very common, it
            // shouldn't matter
            redirectPath = redirectPath + "?" + query;
        }
        response.sendRedirect(redirectPath);
        request.getContext().logAccess(request, response, 0, true);
        return false;
    }
    // Filter trace method
    if (!connector.getAllowTrace() && req.method().equalsIgnoreCase("TRACE")) {
        Wrapper wrapper = request.getWrapper();
        String header = null;
        if (wrapper != null) {
            String[] methods = wrapper.getServletMethods();
            if (methods != null) {
                for (String method : methods) {
                    if ("TRACE".equals(method)) {
                        continue;
                    }
                    if (header == null) {
                        header = method;
                    } else {
                        header += ", " + method;
                    }
                }
            }
        }
        if (header != null) {
            res.addHeader("Allow", header);
        }
        response.sendError(405, "TRACE method is not allowed");
        // Safe to skip the remainder of this method.
        return true;
    }
    doConnectorAuthenticationAuthorization(req, request);
    return true;
}
Also used : Context(org.apache.catalina.Context) Wrapper(org.apache.catalina.Wrapper) MessageBytes(org.apache.tomcat.util.buf.MessageBytes) IOException(java.io.IOException) CharChunk(org.apache.tomcat.util.buf.CharChunk)

Example 17 with CharChunk

use of org.apache.tomcat.util.buf.CharChunk in project tomcat by apache.

the class CoyoteAdapter method convertURI.

/**
 * Character conversion of the URI.
 *
 * @param uri MessageBytes object containing the URI
 * @param request The Servlet request object
 * @throws IOException if a IO exception occurs sending an error to the client
 */
protected void convertURI(MessageBytes uri, Request request) throws IOException {
    ByteChunk bc = uri.getByteChunk();
    int length = bc.getLength();
    CharChunk cc = uri.getCharChunk();
    cc.allocate(length, -1);
    Charset charset = connector.getURICharset();
    B2CConverter conv = request.getURIConverter();
    if (conv == null) {
        conv = new B2CConverter(charset, false);
        request.setURIConverter(conv);
    } else {
        conv.recycle();
    }
    try {
        conv.convert(bc, cc, true);
        uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());
    } catch (IOException ioe) {
        // Should never happen as B2CConverter should replace
        // problematic characters
        request.getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST);
    }
}
Also used : B2CConverter(org.apache.tomcat.util.buf.B2CConverter) ByteChunk(org.apache.tomcat.util.buf.ByteChunk) Charset(java.nio.charset.Charset) IOException(java.io.IOException) CharChunk(org.apache.tomcat.util.buf.CharChunk)

Aggregations

CharChunk (org.apache.tomcat.util.buf.CharChunk)17 ByteChunk (org.apache.tomcat.util.buf.ByteChunk)7 IOException (java.io.IOException)5 MessageBytes (org.apache.tomcat.util.buf.MessageBytes)5 Wrapper (org.apache.catalina.Wrapper)3 ServletException (jakarta.servlet.ServletException)2 InvocationTargetException (java.lang.reflect.InvocationTargetException)2 MalformedURLException (java.net.MalformedURLException)2 Charset (java.nio.charset.Charset)2 NamingException (javax.naming.NamingException)2 B2CConverter (org.apache.tomcat.util.buf.B2CConverter)2 Cookie (jakarta.servlet.http.Cookie)1 HttpServletMapping (jakarta.servlet.http.HttpServletMapping)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 Principal (java.security.Principal)1 ArrayList (java.util.ArrayList)1 ServletException (javax.servlet.ServletException)1 Context (org.apache.catalina.Context)1 LifecycleException (org.apache.catalina.LifecycleException)1 Pipeline (org.apache.catalina.Pipeline)1