public void invoke()

in java/org/apache/catalina/valves/rewrite/RewriteValve.java [296:602]


    public void invoke(Request request, Response response) throws IOException, ServletException {

        if (!getEnabled() || rules == null || rules.length == 0) {
            getNext().invoke(request, response);
            return;
        }

        if (Boolean.TRUE.equals(invoked.get())) {
            try {
                getNext().invoke(request, response);
            } finally {
                invoked.set(null);
            }
            return;
        }

        try {

            Resolver resolver = new ResolverImpl(request);

            invoked.set(Boolean.TRUE);

            // As long as MB isn't a char sequence or affiliated, this has to be converted to a string
            Charset uriCharset = request.getConnector().getURICharset();
            String originalQueryStringEncoded = request.getQueryString();
            MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB();
            urlMB.toChars();
            CharSequence urlDecoded = urlMB.getCharChunk();

            /*
             * The URL presented to the rewrite valve is the URL that is used for request mapping. That URL has been
             * processed to: remove path parameters; remove the query string; decode; and normalize the URL. It may
             * contain literal '%', '?' and/or ';' characters at this point.
             *
             * The re-write rules need to be able to process URLs with literal '?' characters and add query strings
             * without the two becoming confused. The re-write rules also need to be able to insert literal '%'
             * characters without them being confused with %nn encoding.
             *
             * To meet these requirement, the URL is processed as follows.
             *
             * Step 1. The URL is partially re-encoded by encodeForRewrite(). This method encodes any literal '%', ';'
             * and/or '?' characters in the URL using the standard %nn form.
             *
             * Step 2. The re-write processing runs with the provided re-write rules against the partially encoded URL.
             * If a re-write rule needs to insert a literal '%', ';' or '?', it must do so in %nn encoded form.
             *
             * Step 3. The URL (and query string if present) is re-encoded using the re-write specific encoders
             * (REWRITE_DEFAULT_ENCODER and REWRITE_QUERY_ENCODER) that behave the same was as the standard encoders
             * apart from '%' being treated as a safe character. This prevents double encoding of any '%' characters
             * present in the URL from steps 1 or 2.
             */

            // Step 1. Encode URL for processing by the re-write rules.
            CharSequence urlRewriteEncoded = encodeForRewrite(urlDecoded);
            CharSequence host = request.getServerName();
            boolean rewritten = false;
            boolean done = false;
            boolean qsa = false;
            boolean qsd = false;
            boolean valveSkip = false;

            // Step 2. Process the URL using the re-write rules.
            for (int i = 0; i < rules.length; i++) {
                RewriteRule rule = rules[i];
                CharSequence test = (rule.isHost()) ? host : urlRewriteEncoded;
                CharSequence newtest = rule.evaluate(test, resolver);
                if (newtest != null && !Objects.equals(test.toString(), newtest.toString())) {
                    if (containerLog.isTraceEnabled()) {
                        containerLog.trace(
                                "Rewrote " + test + " as " + newtest + " with rule pattern " + rule.getPatternString());
                    }
                    if (rule.isHost()) {
                        host = newtest;
                    } else {
                        urlRewriteEncoded = newtest;
                    }
                    rewritten = true;
                }

                // Check QSA before the final reply
                if (!qsa && newtest != null && rule.isQsappend()) {
                    qsa = true;
                }

                if (!qsd && newtest != null && rule.isQsdiscard()) {
                    qsd = true;
                }

                if (!valveSkip && newtest != null && rule.isValveSkip()) {
                    valveSkip = true;
                }

                // Final reply

                // - forbidden
                if (rule.isForbidden() && newtest != null) {
                    response.sendError(HttpServletResponse.SC_FORBIDDEN);
                    done = true;
                    break;
                }
                // - gone
                if (rule.isGone() && newtest != null) {
                    response.sendError(HttpServletResponse.SC_GONE);
                    done = true;
                    break;
                }

                // - redirect (code)
                if (rule.isRedirect() && newtest != null) {
                    // Append the query string to the url if there is one and it
                    // hasn't been rewritten
                    String urlStringRewriteEncoded = urlRewriteEncoded.toString();
                    int index = urlStringRewriteEncoded.indexOf('?');
                    String rewrittenQueryStringRewriteEncoded;
                    if (index == -1) {
                        rewrittenQueryStringRewriteEncoded = null;
                    } else {
                        rewrittenQueryStringRewriteEncoded = urlStringRewriteEncoded.substring(index + 1);
                        urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, index);
                    }

                    // Step 3. Complete the 2nd stage to encoding.
                    StringBuilder urlStringEncoded =
                            new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));

                    if (!qsd && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
                        if (rewrittenQueryStringRewriteEncoded == null) {
                            urlStringEncoded.append('?');
                            urlStringEncoded.append(originalQueryStringEncoded);
                        } else {
                            if (qsa) {
                                // if qsa is specified append the query
                                urlStringEncoded.append('?');
                                urlStringEncoded.append(
                                        REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
                                urlStringEncoded.append('&');
                                urlStringEncoded.append(originalQueryStringEncoded);
                            } else if (index == urlStringEncoded.length() - 1) {
                                // if the ? is the last character delete it, its only purpose was to
                                // prevent the rewrite module from appending the query string
                                urlStringEncoded.deleteCharAt(index);
                            } else {
                                urlStringEncoded.append('?');
                                urlStringEncoded.append(
                                        REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
                            }
                        }
                    } else if (rewrittenQueryStringRewriteEncoded != null) {
                        urlStringEncoded.append('?');
                        urlStringEncoded
                                .append(REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
                    }

                    // Insert the context if
                    // 1. this valve is associated with a context
                    // 2. the url starts with a leading slash
                    // 3. the url isn't absolute
                    if (context && urlStringEncoded.charAt(0) == '/' && !UriUtil.hasScheme(urlStringEncoded)) {
                        urlStringEncoded.insert(0, request.getContext().getEncodedPath());
                    }
                    if (rule.isNoescape()) {
                        response.sendRedirect(UDecoder.URLDecode(urlStringEncoded.toString(), uriCharset));
                    } else {
                        response.sendRedirect(urlStringEncoded.toString());
                    }
                    response.setStatus(rule.getRedirectCode());
                    done = true;
                    break;
                }

                // Reply modification

                // - cookie
                if (rule.isCookie() && newtest != null) {
                    Cookie cookie = new Cookie(rule.getCookieName(), rule.getCookieResult());
                    cookie.setDomain(rule.getCookieDomain());
                    cookie.setMaxAge(rule.getCookieLifetime());
                    cookie.setPath(rule.getCookiePath());
                    cookie.setSecure(rule.isCookieSecure());
                    cookie.setHttpOnly(rule.isCookieHttpOnly());
                    response.addCookie(cookie);
                }
                // - env (note: this sets a request attribute)
                if (rule.isEnv() && newtest != null) {
                    for (int j = 0; j < rule.getEnvSize(); j++) {
                        request.setAttribute(rule.getEnvName(j), rule.getEnvResult(j));
                    }
                }
                // - content type (note: this will not force the content type, use a filter
                // to do that)
                if (rule.isType() && newtest != null) {
                    response.setContentType(rule.getTypeValue());
                }

                // Control flow processing

                // - chain (skip remaining chained rules if this one does not match)
                if (rule.isChain() && newtest == null) {
                    for (int j = i; j < rules.length; j++) {
                        if (!rules[j].isChain()) {
                            i = j;
                            break;
                        }
                    }
                    continue;
                }
                // - last (stop rewriting here)
                if (rule.isLast() && newtest != null) {
                    break;
                }
                // - next (redo again)
                if (rule.isNext() && newtest != null) {
                    i = 0;
                    continue;
                }
                // - skip (n rules)
                if (newtest != null) {
                    i += rule.getSkip();
                }

            }

            if (rewritten) {
                if (!done) {
                    // See if we need to replace the query string
                    String urlStringRewriteEncoded = urlRewriteEncoded.toString();
                    String queryStringRewriteEncoded = null;
                    int queryIndex = urlStringRewriteEncoded.indexOf('?');
                    if (queryIndex != -1) {
                        queryStringRewriteEncoded = urlStringRewriteEncoded.substring(queryIndex + 1);
                        urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, queryIndex);
                    }
                    // Parse path parameters from rewrite production and populate request path parameters
                    urlStringRewriteEncoded = org.apache.catalina.util.RequestUtil.stripPathParams(urlStringRewriteEncoded, request);
                    // Save the current context path before re-writing starts
                    String contextPath = null;
                    if (context) {
                        contextPath = request.getContextPath();
                    }
                    // Populated the encoded (i.e. undecoded) requestURI
                    request.getCoyoteRequest().requestURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
                    CharChunk chunk = request.getCoyoteRequest().requestURI().getCharChunk();
                    if (context) {
                        // This is neither decoded nor normalized
                        chunk.append(contextPath);
                    }

                    // Step 3. Complete the 2nd stage to encoding.
                    chunk.append(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
                    // Decoded and normalized URI
                    // Rewriting may have denormalized the URL
                    urlStringRewriteEncoded = RequestUtil.normalize(urlStringRewriteEncoded);
                    request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
                    chunk = request.getCoyoteRequest().decodedURI().getCharChunk();
                    if (context) {
                        // This is decoded and normalized
                        chunk.append(request.getServletContext().getContextPath());
                    }
                    chunk.append(URLDecoder.decode(urlStringRewriteEncoded, uriCharset));
                    // Set the new Query if there is one
                    if (queryStringRewriteEncoded != null) {
                        request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
                        chunk = request.getCoyoteRequest().queryString().getCharChunk();
                        chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset));
                        if (qsa && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
                            chunk.append('&');
                            chunk.append(originalQueryStringEncoded);
                        }
                    }
                    // Set the new host if it changed
                    if (!host.equals(request.getServerName())) {
                        request.getCoyoteRequest().serverName().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
                        chunk = request.getCoyoteRequest().serverName().getCharChunk();
                        chunk.append(host.toString());
                    }
                    request.getMappingData().recycle();
                    // Reinvoke the whole request recursively
                    Connector connector = request.getConnector();
                    try {
                        if (!connector.getProtocolHandler().getAdapter().prepare(request.getCoyoteRequest(),
                                response.getCoyoteResponse())) {
                            return;
                        }
                    } catch (Exception e) {
                        // This doesn't actually happen in the Catalina adapter implementation
                    }
                    Pipeline pipeline = connector.getService().getContainer().getPipeline();
                    request.setAsyncSupported(pipeline.isAsyncSupported());
                    pipeline.getFirst().invoke(request, response);
                }
            } else {
                Valve next = getNext();
                if (valveSkip) {
                    next = next.getNext();
                    if (next == null) {
                        // Ignore and invoke the next valve normally
                        next = getNext();
                    }
                }
                next.invoke(request, response);
            }

        } finally {
            invoked.set(null);
        }

    }