init
This commit is contained in:
563
java/org/apache/catalina/valves/rewrite/RewriteRule.java
Normal file
563
java/org/apache/catalina/valves/rewrite/RewriteRule.java
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.valves.rewrite;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RewriteRule {
|
||||
|
||||
protected RewriteCond[] conditions = new RewriteCond[0];
|
||||
|
||||
protected ThreadLocal<Pattern> pattern = new ThreadLocal<>();
|
||||
protected Substitution substitution = null;
|
||||
|
||||
protected String patternString = null;
|
||||
protected String substitutionString = null;
|
||||
protected String flagsString = null;
|
||||
protected boolean positive = true;
|
||||
|
||||
public void parse(Map<String, RewriteMap> maps) {
|
||||
// Parse the substitution
|
||||
if (!"-".equals(substitutionString)) {
|
||||
substitution = new Substitution();
|
||||
substitution.setSub(substitutionString);
|
||||
substitution.parse(maps);
|
||||
substitution.setEscapeBackReferences(isEscapeBackReferences());
|
||||
}
|
||||
// Parse the pattern
|
||||
if (patternString.startsWith("!")) {
|
||||
positive = false;
|
||||
patternString = patternString.substring(1);
|
||||
}
|
||||
int flags = 0;
|
||||
if (isNocase()) {
|
||||
flags |= Pattern.CASE_INSENSITIVE;
|
||||
}
|
||||
Pattern.compile(patternString, flags);
|
||||
// Parse conditions
|
||||
for (int i = 0; i < conditions.length; i++) {
|
||||
conditions[i].parse(maps);
|
||||
}
|
||||
// Parse flag which have substitution values
|
||||
if (isEnv()) {
|
||||
for (int i = 0; i < envValue.size(); i++) {
|
||||
Substitution newEnvSubstitution = new Substitution();
|
||||
newEnvSubstitution.setSub(envValue.get(i));
|
||||
newEnvSubstitution.parse(maps);
|
||||
envSubstitution.add(newEnvSubstitution);
|
||||
envResult.add(new ThreadLocal<String>());
|
||||
}
|
||||
}
|
||||
if (isCookie()) {
|
||||
cookieSubstitution = new Substitution();
|
||||
cookieSubstitution.setSub(cookieValue);
|
||||
cookieSubstitution.parse(maps);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCondition(RewriteCond condition) {
|
||||
RewriteCond[] conditions = Arrays.copyOf(this.conditions, this.conditions.length + 1);
|
||||
conditions[this.conditions.length] = condition;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the rule based on the context
|
||||
* @param url The char sequence
|
||||
* @param resolver Property resolver
|
||||
* @return <code>null</code> if no rewrite took place
|
||||
*/
|
||||
public CharSequence evaluate(CharSequence url, Resolver resolver) {
|
||||
Pattern pattern = this.pattern.get();
|
||||
if (pattern == null) {
|
||||
// Parse the pattern
|
||||
int flags = 0;
|
||||
if (isNocase()) {
|
||||
flags |= Pattern.CASE_INSENSITIVE;
|
||||
}
|
||||
pattern = Pattern.compile(patternString, flags);
|
||||
this.pattern.set(pattern);
|
||||
}
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
// Use XOR
|
||||
if (positive ^ matcher.matches()) {
|
||||
// Evaluation done
|
||||
return null;
|
||||
}
|
||||
// Evaluate conditions
|
||||
boolean done = false;
|
||||
boolean rewrite = true;
|
||||
Matcher lastMatcher = null;
|
||||
int pos = 0;
|
||||
while (!done) {
|
||||
if (pos < conditions.length) {
|
||||
rewrite = conditions[pos].evaluate(matcher, lastMatcher, resolver);
|
||||
if (rewrite) {
|
||||
Matcher lastMatcher2 = conditions[pos].getMatcher();
|
||||
if (lastMatcher2 != null) {
|
||||
lastMatcher = lastMatcher2;
|
||||
}
|
||||
while (pos < conditions.length && conditions[pos].isOrnext()) {
|
||||
pos++;
|
||||
}
|
||||
} else if (!conditions[pos].isOrnext()) {
|
||||
done = true;
|
||||
}
|
||||
pos++;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
// Use the substitution to rewrite the url
|
||||
if (rewrite) {
|
||||
if (isEnv()) {
|
||||
for (int i = 0; i < envSubstitution.size(); i++) {
|
||||
envResult.get(i).set(envSubstitution.get(i).evaluate(matcher, lastMatcher, resolver));
|
||||
}
|
||||
}
|
||||
if (isCookie()) {
|
||||
cookieResult.set(cookieSubstitution.evaluate(matcher, lastMatcher, resolver));
|
||||
}
|
||||
if (substitution != null) {
|
||||
return substitution.evaluate(matcher, lastMatcher, resolver);
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* String representation.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RewriteRule " + patternString + " " + substitutionString
|
||||
+ ((flagsString != null) ? (" " + flagsString) : "");
|
||||
}
|
||||
|
||||
|
||||
private boolean escapeBackReferences = false;
|
||||
|
||||
/**
|
||||
* This flag chains the current rule with the next rule (which itself
|
||||
* can be chained with the following rule, etc.). This has the following
|
||||
* effect: if a rule matches, then processing continues as usual, i.e.,
|
||||
* the flag has no effect. If the rule does not match, then all following
|
||||
* chained rules are skipped. For instance, use it to remove the ``.www''
|
||||
* part inside a per-directory rule set when you let an external redirect
|
||||
* happen (where the ``.www'' part should not to occur!).
|
||||
*/
|
||||
protected boolean chain = false;
|
||||
|
||||
/**
|
||||
* This sets a cookie on the client's browser. The cookie's name is
|
||||
* specified by NAME and the value is VAL. The domain field is the domain
|
||||
* of the cookie, such as '.apache.org',the optional lifetime
|
||||
* is the lifetime of the cookie in minutes, and the optional path is the
|
||||
* path of the cookie
|
||||
*/
|
||||
protected boolean cookie = false;
|
||||
protected String cookieName = null;
|
||||
protected String cookieValue = null;
|
||||
protected String cookieDomain = null;
|
||||
protected int cookieLifetime = -1;
|
||||
protected String cookiePath = null;
|
||||
protected boolean cookieSecure = false;
|
||||
protected boolean cookieHttpOnly = false;
|
||||
protected Substitution cookieSubstitution = null;
|
||||
protected ThreadLocal<String> cookieResult = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* This forces a request attribute named VAR to be set to the value VAL,
|
||||
* where VAL can contain regexp back references $N and %N which will be
|
||||
* expanded. Multiple env flags are allowed.
|
||||
*/
|
||||
protected boolean env = false;
|
||||
protected ArrayList<String> envName = new ArrayList<>();
|
||||
protected ArrayList<String> envValue = new ArrayList<>();
|
||||
protected ArrayList<Substitution> envSubstitution = new ArrayList<>();
|
||||
protected ArrayList<ThreadLocal<String>> envResult = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* This forces the current URL to be forbidden, i.e., it immediately sends
|
||||
* back an HTTP response of 403 (FORBIDDEN). Use this flag in conjunction
|
||||
* with appropriate RewriteConds to conditionally block some URLs.
|
||||
*/
|
||||
protected boolean forbidden = false;
|
||||
|
||||
/**
|
||||
* This forces the current URL to be gone, i.e., it immediately sends
|
||||
* back an HTTP response of 410 (GONE). Use this flag to mark pages which
|
||||
* no longer exist as gone.
|
||||
*/
|
||||
protected boolean gone = false;
|
||||
|
||||
/**
|
||||
* Host. This means this rule and its associated conditions will apply to
|
||||
* host, allowing host rewriting (ex: redirecting internally *.foo.com to
|
||||
* bar.foo.com).
|
||||
*/
|
||||
protected boolean host = false;
|
||||
|
||||
/**
|
||||
* Stop the rewriting process here and don't apply any more rewriting
|
||||
* rules. This corresponds to the Perl last command or the break command
|
||||
* from the C language. Use this flag to prevent the currently rewritten
|
||||
* URL from being rewritten further by following rules. For example, use
|
||||
* it to rewrite the root-path URL ('/') to a real one, e.g., '/e/www/'.
|
||||
*/
|
||||
protected boolean last = false;
|
||||
|
||||
/**
|
||||
* Re-run the rewriting process (starting again with the first rewriting
|
||||
* rule). Here the URL to match is again not the original URL but the URL
|
||||
* from the last rewriting rule. This corresponds to the Perl next
|
||||
* command or the continue command from the C language. Use this flag to
|
||||
* restart the rewriting process, i.e., to immediately go to the top of
|
||||
* the loop. But be careful not to create an infinite loop!
|
||||
*/
|
||||
protected boolean next = false;
|
||||
|
||||
/**
|
||||
* This makes the Pattern case-insensitive, i.e., there is no difference
|
||||
* between 'A-Z' and 'a-z' when Pattern is matched against the current
|
||||
* URL.
|
||||
*/
|
||||
protected boolean nocase = false;
|
||||
|
||||
/**
|
||||
* This flag keeps mod_rewrite from applying the usual URI escaping rules
|
||||
* to the result of a rewrite. Ordinarily, special characters (such as
|
||||
* '%', '$', ';', and so on) will be escaped into their hexcode
|
||||
* equivalents ('%25', '%24', and '%3B', respectively); this flag
|
||||
* prevents this from being done. This allows percent symbols to appear
|
||||
* in the output, as in
|
||||
* RewriteRule /foo/(.*) /bar?arg=P1\%3d$1 [R,NE]
|
||||
* which would turn '/foo/zed' into a safe request for '/bar?arg=P1=zed'.
|
||||
*/
|
||||
protected boolean noescape = false;
|
||||
|
||||
/**
|
||||
* This flag forces the rewriting engine to skip a rewriting rule if the
|
||||
* current request is an internal sub-request. For instance, sub-requests
|
||||
* occur internally in Apache when mod_include tries to find out
|
||||
* information about possible directory default files (index.xxx). On
|
||||
* sub-requests it is not always useful and even sometimes causes a
|
||||
* failure to if the complete set of rules are applied. Use this flag to
|
||||
* exclude some rules. Use the following rule for your decision: whenever
|
||||
* you prefix some URLs with CGI-scripts to force them to be processed by
|
||||
* the CGI-script, the chance is high that you will run into problems (or
|
||||
* even overhead) on sub-requests. In these cases, use this flag.
|
||||
*/
|
||||
protected boolean nosubreq = false;
|
||||
|
||||
/**
|
||||
* Note: No proxy
|
||||
*/
|
||||
|
||||
/**
|
||||
* Note: No passthrough
|
||||
*/
|
||||
|
||||
/**
|
||||
* This flag forces the rewriting engine to append a query string part in
|
||||
* the substitution string to the existing one instead of replacing it.
|
||||
* Use this when you want to add more data to the query string via
|
||||
* a rewrite rule.
|
||||
*/
|
||||
protected boolean qsappend = false;
|
||||
|
||||
/**
|
||||
* When the requested URI contains a query string, and the target URI does
|
||||
* not, the default behavior of RewriteRule is to copy that query string
|
||||
* to the target URI. Using the [QSD] flag causes the query string
|
||||
* to be discarded.
|
||||
* Using [QSD] and [QSA] together will result in [QSD] taking precedence.
|
||||
*/
|
||||
protected boolean qsdiscard = false;
|
||||
|
||||
/**
|
||||
* Prefix Substitution with http://thishost[:thisport]/ (which makes the
|
||||
* new URL a URI) to force a external redirection. If no code is given
|
||||
* an HTTP response of 302 (FOUND, previously MOVED TEMPORARILY) is used.
|
||||
* If you want to use other response codes in the range 300-399 just
|
||||
* specify them as a number or use one of the following symbolic names:
|
||||
* temp (default), permanent, seeother. Use it for rules which should
|
||||
* canonicalize the URL and give it back to the client, e.g., translate
|
||||
* ``/~'' into ``/u/'' or always append a slash to /u/user, etc. Note:
|
||||
* When you use this flag, make sure that the substitution field is a
|
||||
* valid URL! If not, you are redirecting to an invalid location!
|
||||
* And remember that this flag itself only prefixes the URL with
|
||||
* http://thishost[:thisport]/, rewriting continues. Usually you also
|
||||
* want to stop and do the redirection immediately. To stop the
|
||||
* rewriting you also have to provide the 'L' flag.
|
||||
*/
|
||||
protected boolean redirect = false;
|
||||
protected int redirectCode = 0;
|
||||
|
||||
/**
|
||||
* This flag forces the rewriting engine to skip the next num rules in
|
||||
* sequence when the current rule matches. Use this to make pseudo
|
||||
* if-then-else constructs: The last rule of the then-clause becomes
|
||||
* skip=N where N is the number of rules in the else-clause.
|
||||
* (This is not the same as the 'chain|C' flag!)
|
||||
*/
|
||||
protected int skip = 0;
|
||||
|
||||
/**
|
||||
* Force the MIME-type of the target file to be MIME-type. For instance,
|
||||
* this can be used to setup the content-type based on some conditions.
|
||||
* For example, the following snippet allows .php files to be displayed
|
||||
* by mod_php if they are called with the .phps extension:
|
||||
* RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source]
|
||||
*/
|
||||
protected boolean type = false;
|
||||
protected String typeValue = null;
|
||||
|
||||
public boolean isEscapeBackReferences() {
|
||||
return escapeBackReferences;
|
||||
}
|
||||
public void setEscapeBackReferences(boolean escapeBackReferences) {
|
||||
this.escapeBackReferences = escapeBackReferences;
|
||||
}
|
||||
public boolean isChain() {
|
||||
return chain;
|
||||
}
|
||||
public void setChain(boolean chain) {
|
||||
this.chain = chain;
|
||||
}
|
||||
public RewriteCond[] getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
public void setConditions(RewriteCond[] conditions) {
|
||||
this.conditions = conditions;
|
||||
}
|
||||
public boolean isCookie() {
|
||||
return cookie;
|
||||
}
|
||||
public void setCookie(boolean cookie) {
|
||||
this.cookie = cookie;
|
||||
}
|
||||
public String getCookieName() {
|
||||
return cookieName;
|
||||
}
|
||||
public void setCookieName(String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
public String getCookieValue() {
|
||||
return cookieValue;
|
||||
}
|
||||
public void setCookieValue(String cookieValue) {
|
||||
this.cookieValue = cookieValue;
|
||||
}
|
||||
public String getCookieResult() {
|
||||
return cookieResult.get();
|
||||
}
|
||||
public boolean isEnv() {
|
||||
return env;
|
||||
}
|
||||
public int getEnvSize() {
|
||||
return envName.size();
|
||||
}
|
||||
public void setEnv(boolean env) {
|
||||
this.env = env;
|
||||
}
|
||||
public String getEnvName(int i) {
|
||||
return envName.get(i);
|
||||
}
|
||||
public void addEnvName(String envName) {
|
||||
this.envName.add(envName);
|
||||
}
|
||||
public String getEnvValue(int i) {
|
||||
return envValue.get(i);
|
||||
}
|
||||
public void addEnvValue(String envValue) {
|
||||
this.envValue.add(envValue);
|
||||
}
|
||||
public String getEnvResult(int i) {
|
||||
return envResult.get(i).get();
|
||||
}
|
||||
public boolean isForbidden() {
|
||||
return forbidden;
|
||||
}
|
||||
public void setForbidden(boolean forbidden) {
|
||||
this.forbidden = forbidden;
|
||||
}
|
||||
public boolean isGone() {
|
||||
return gone;
|
||||
}
|
||||
public void setGone(boolean gone) {
|
||||
this.gone = gone;
|
||||
}
|
||||
public boolean isLast() {
|
||||
return last;
|
||||
}
|
||||
public void setLast(boolean last) {
|
||||
this.last = last;
|
||||
}
|
||||
public boolean isNext() {
|
||||
return next;
|
||||
}
|
||||
public void setNext(boolean next) {
|
||||
this.next = next;
|
||||
}
|
||||
public boolean isNocase() {
|
||||
return nocase;
|
||||
}
|
||||
public void setNocase(boolean nocase) {
|
||||
this.nocase = nocase;
|
||||
}
|
||||
public boolean isNoescape() {
|
||||
return noescape;
|
||||
}
|
||||
public void setNoescape(boolean noescape) {
|
||||
this.noescape = noescape;
|
||||
}
|
||||
public boolean isNosubreq() {
|
||||
return nosubreq;
|
||||
}
|
||||
public void setNosubreq(boolean nosubreq) {
|
||||
this.nosubreq = nosubreq;
|
||||
}
|
||||
public boolean isQsappend() {
|
||||
return qsappend;
|
||||
}
|
||||
public void setQsappend(boolean qsappend) {
|
||||
this.qsappend = qsappend;
|
||||
}
|
||||
public final boolean isQsdiscard() {
|
||||
return qsdiscard;
|
||||
}
|
||||
public final void setQsdiscard(boolean qsdiscard) {
|
||||
this.qsdiscard = qsdiscard;
|
||||
}
|
||||
public boolean isRedirect() {
|
||||
return redirect;
|
||||
}
|
||||
public void setRedirect(boolean redirect) {
|
||||
this.redirect = redirect;
|
||||
}
|
||||
public int getRedirectCode() {
|
||||
return redirectCode;
|
||||
}
|
||||
public void setRedirectCode(int redirectCode) {
|
||||
this.redirectCode = redirectCode;
|
||||
}
|
||||
public int getSkip() {
|
||||
return skip;
|
||||
}
|
||||
public void setSkip(int skip) {
|
||||
this.skip = skip;
|
||||
}
|
||||
public Substitution getSubstitution() {
|
||||
return substitution;
|
||||
}
|
||||
public void setSubstitution(Substitution substitution) {
|
||||
this.substitution = substitution;
|
||||
}
|
||||
public boolean isType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(boolean type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getTypeValue() {
|
||||
return typeValue;
|
||||
}
|
||||
public void setTypeValue(String typeValue) {
|
||||
this.typeValue = typeValue;
|
||||
}
|
||||
|
||||
public String getPatternString() {
|
||||
return patternString;
|
||||
}
|
||||
|
||||
public void setPatternString(String patternString) {
|
||||
this.patternString = patternString;
|
||||
}
|
||||
|
||||
public String getSubstitutionString() {
|
||||
return substitutionString;
|
||||
}
|
||||
|
||||
public void setSubstitutionString(String substitutionString) {
|
||||
this.substitutionString = substitutionString;
|
||||
}
|
||||
|
||||
public final String getFlagsString() {
|
||||
return flagsString;
|
||||
}
|
||||
|
||||
public final void setFlagsString(String flagsString) {
|
||||
this.flagsString = flagsString;
|
||||
}
|
||||
|
||||
public boolean isHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(boolean host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String getCookieDomain() {
|
||||
return cookieDomain;
|
||||
}
|
||||
|
||||
public void setCookieDomain(String cookieDomain) {
|
||||
this.cookieDomain = cookieDomain;
|
||||
}
|
||||
|
||||
public int getCookieLifetime() {
|
||||
return cookieLifetime;
|
||||
}
|
||||
|
||||
public void setCookieLifetime(int cookieLifetime) {
|
||||
this.cookieLifetime = cookieLifetime;
|
||||
}
|
||||
|
||||
public String getCookiePath() {
|
||||
return cookiePath;
|
||||
}
|
||||
|
||||
public void setCookiePath(String cookiePath) {
|
||||
this.cookiePath = cookiePath;
|
||||
}
|
||||
|
||||
public boolean isCookieSecure() {
|
||||
return cookieSecure;
|
||||
}
|
||||
|
||||
public void setCookieSecure(boolean cookieSecure) {
|
||||
this.cookieSecure = cookieSecure;
|
||||
}
|
||||
|
||||
public boolean isCookieHttpOnly() {
|
||||
return cookieHttpOnly;
|
||||
}
|
||||
|
||||
public void setCookieHttpOnly(boolean cookieHttpOnly) {
|
||||
this.cookieHttpOnly = cookieHttpOnly;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user