This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
# 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.
rewriteValve.closeError=Error closing configuration
rewriteValve.invalidFlags=Invalid flag in [{0}] flags [{1}]
rewriteValve.invalidLine=Invalid line [{0}]
rewriteValve.invalidMapClassName=Invalid map class name [{0}]
rewriteValve.readError=Error reading configuration

View File

@@ -0,0 +1,20 @@
# 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.
rewriteValve.closeError=Erreur lors de la fermeture de la configuration
rewriteValve.invalidFlags=Indicateur invalide dans [{0}] indicateurs [{1}]
rewriteValve.invalidLine=Ligne invalide [{0}]
rewriteValve.invalidMapClassName=Le nom de la classe [{0}] de la structure est invalide
rewriteValve.readError=Erreur lors de la lecture de la configuration

View File

@@ -0,0 +1,20 @@
# 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.
rewriteValve.closeError=構成を閉じる際のエラー
rewriteValve.invalidFlags=[{0}]に無効なフラグ [{1}]があります。
rewriteValve.invalidLine=無効な行[{0}]
rewriteValve.invalidMapClassName=Mapクラス名[{0}]が無効です。
rewriteValve.readError=構成の読み取りエラー

View File

@@ -0,0 +1,20 @@
# 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.
rewriteValve.closeError=설정 리소스의 스트림을 닫는 중 오류 발생
rewriteValve.invalidFlags=[{0}] 내에 유효하지 않은 플래그입니다 (플래그들: [{1}]).
rewriteValve.invalidLine=유효하지 않은 행 [{0}]
rewriteValve.invalidMapClassName=유효하지 않은 Map 클래스 이름: [{0}]
rewriteValve.readError=설정을 읽는 도중 오류 발생

View File

@@ -0,0 +1,16 @@
# 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.
rewriteValve.readError=读取配置时发生异常

View File

@@ -0,0 +1,48 @@
/*
* 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.nio.charset.Charset;
/**
* Resolver abstract class.
*/
public abstract class Resolver {
public abstract String resolve(String key);
public String resolveEnv(String key) {
return System.getProperty(key);
}
public abstract String resolveSsl(String key);
public abstract String resolveHttp(String key);
public abstract boolean resolveResource(int type, String name);
/**
* @return The name of the encoding to use to %nn encode URIs
*
* @deprecated This will be removed in Tomcat 9.0.x
*/
@Deprecated
public abstract String getUriEncoding();
public abstract Charset getUriCharset();
}

View File

@@ -0,0 +1,191 @@
/*
* 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.nio.charset.Charset;
import java.util.Calendar;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.connector.Request;
import org.apache.tomcat.util.http.FastHttpDateFormat;
public class ResolverImpl extends Resolver {
protected Request request = null;
public ResolverImpl(Request request) {
this.request = request;
}
/**
* The following are not implemented:
* - SERVER_ADMIN
* - API_VERSION
* - IS_SUBREQ
*/
@Override
public String resolve(String key) {
if (key.equals("HTTP_USER_AGENT")) {
return request.getHeader("user-agent");
} else if (key.equals("HTTP_REFERER")) {
return request.getHeader("referer");
} else if (key.equals("HTTP_COOKIE")) {
return request.getHeader("cookie");
} else if (key.equals("HTTP_FORWARDED")) {
return request.getHeader("forwarded");
} else if (key.equals("HTTP_HOST")) {
String host = request.getHeader("host");
if (host != null) {
int index = host.indexOf(':');
if (index != -1) {
host = host.substring(0, index);
}
}
return host;
} else if (key.equals("HTTP_PROXY_CONNECTION")) {
return request.getHeader("proxy-connection");
} else if (key.equals("HTTP_ACCEPT")) {
return request.getHeader("accept");
} else if (key.equals("REMOTE_ADDR")) {
return request.getRemoteAddr();
} else if (key.equals("REMOTE_HOST")) {
return request.getRemoteHost();
} else if (key.equals("REMOTE_PORT")) {
return String.valueOf(request.getRemotePort());
} else if (key.equals("REMOTE_USER")) {
return request.getRemoteUser();
} else if (key.equals("REMOTE_IDENT")) {
return request.getRemoteUser();
} else if (key.equals("REQUEST_METHOD")) {
return request.getMethod();
} else if (key.equals("SCRIPT_FILENAME")) {
return request.getServletContext().getRealPath(request.getServletPath());
} else if (key.equals("REQUEST_PATH")) {
return request.getRequestPathMB().toString();
} else if (key.equals("CONTEXT_PATH")) {
return request.getContextPath();
} else if (key.equals("SERVLET_PATH")) {
return emptyStringIfNull(request.getServletPath());
} else if (key.equals("PATH_INFO")) {
return emptyStringIfNull(request.getPathInfo());
} else if (key.equals("QUERY_STRING")) {
return emptyStringIfNull(request.getQueryString());
} else if (key.equals("AUTH_TYPE")) {
return request.getAuthType();
} else if (key.equals("DOCUMENT_ROOT")) {
return request.getServletContext().getRealPath("/");
} else if (key.equals("SERVER_NAME")) {
return request.getLocalName();
} else if (key.equals("SERVER_ADDR")) {
return request.getLocalAddr();
} else if (key.equals("SERVER_PORT")) {
return String.valueOf(request.getLocalPort());
} else if (key.equals("SERVER_PROTOCOL")) {
return request.getProtocol();
} else if (key.equals("SERVER_SOFTWARE")) {
return "tomcat";
} else if (key.equals("THE_REQUEST")) {
return request.getMethod() + " " + request.getRequestURI()
+ " " + request.getProtocol();
} else if (key.equals("REQUEST_URI")) {
return request.getRequestURI();
} else if (key.equals("REQUEST_FILENAME")) {
return request.getPathTranslated();
} else if (key.equals("HTTPS")) {
return request.isSecure() ? "on" : "off";
} else if (key.equals("TIME_YEAR")) {
return String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
} else if (key.equals("TIME_MON")) {
return String.valueOf(Calendar.getInstance().get(Calendar.MONTH));
} else if (key.equals("TIME_DAY")) {
return String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
} else if (key.equals("TIME_HOUR")) {
return String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
} else if (key.equals("TIME_MIN")) {
return String.valueOf(Calendar.getInstance().get(Calendar.MINUTE));
} else if (key.equals("TIME_SEC")) {
return String.valueOf(Calendar.getInstance().get(Calendar.SECOND));
} else if (key.equals("TIME_WDAY")) {
return String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_WEEK));
} else if (key.equals("TIME")) {
return FastHttpDateFormat.getCurrentDate();
}
return null;
}
@Override
public String resolveEnv(String key) {
Object result = request.getAttribute(key);
return (result != null) ? result.toString() : System.getProperty(key);
}
@Override
public String resolveSsl(String key) {
// FIXME: Implement SSL environment variables
return null;
}
@Override
public String resolveHttp(String key) {
String header = request.getHeader(key);
if (header == null) {
return "";
} else {
return header;
}
}
@Override
public boolean resolveResource(int type, String name) {
WebResourceRoot resources = request.getContext().getResources();
WebResource resource = resources.getResource(name);
if (!resource.exists()) {
return false;
} else {
switch (type) {
case 0:
return resource.isDirectory();
case 1:
return resource.isFile();
case 2:
return resource.isFile() && resource.getContentLength() > 0;
default:
return false;
}
}
}
private static final String emptyStringIfNull(String value) {
if (value == null) {
return "";
} else {
return value;
}
}
@Override
@Deprecated
public String getUriEncoding() {
return request.getConnector().getURIEncoding();
}
@Override
public Charset getUriCharset() {
return request.getConnector().getURICharset();
}
}

View File

@@ -0,0 +1,239 @@
/*
* 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.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RewriteCond {
public abstract static class Condition {
public abstract boolean evaluate(String value, Resolver resolver);
}
public static class PatternCondition extends Condition {
public Pattern pattern;
private ThreadLocal<Matcher> matcher = new ThreadLocal<>();
@Override
public boolean evaluate(String value, Resolver resolver) {
Matcher m = pattern.matcher(value);
if (m.matches()) {
matcher.set(m);
return true;
} else {
return false;
}
}
public Matcher getMatcher() {
return matcher.get();
}
}
public static class LexicalCondition extends Condition {
/**
* -1: &lt;
* 0: =
* 1: &gt;
*/
public int type = 0;
public String condition;
@Override
public boolean evaluate(String value, Resolver resolver) {
int result = value.compareTo(condition);
switch (type) {
case -1:
return (result < 0);
case 0:
return (result == 0);
case 1:
return (result > 0);
default:
return false;
}
}
}
public static class ResourceCondition extends Condition {
/**
* 0: -d (is directory ?)
* 1: -f (is regular file ?)
* 2: -s (is regular file with size ?)
*/
public int type = 0;
@Override
public boolean evaluate(String value, Resolver resolver) {
return resolver.resolveResource(type, value);
}
}
protected String testString = null;
protected String condPattern = null;
protected String flagsString = null;
public String getCondPattern() {
return condPattern;
}
public void setCondPattern(String condPattern) {
this.condPattern = condPattern;
}
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
public final String getFlagsString() {
return flagsString;
}
public final void setFlagsString(String flagsString) {
this.flagsString = flagsString;
}
public void parse(Map<String, RewriteMap> maps) {
test = new Substitution();
test.setSub(testString);
test.parse(maps);
if (condPattern.startsWith("!")) {
positive = false;
condPattern = condPattern.substring(1);
}
if (condPattern.startsWith("<")) {
LexicalCondition ncondition = new LexicalCondition();
ncondition.type = -1;
ncondition.condition = condPattern.substring(1);
this.condition = ncondition;
} else if (condPattern.startsWith(">")) {
LexicalCondition ncondition = new LexicalCondition();
ncondition.type = 1;
ncondition.condition = condPattern.substring(1);
this.condition = ncondition;
} else if (condPattern.startsWith("=")) {
LexicalCondition ncondition = new LexicalCondition();
ncondition.type = 0;
ncondition.condition = condPattern.substring(1);
this.condition = ncondition;
} else if (condPattern.equals("-d")) {
ResourceCondition ncondition = new ResourceCondition();
ncondition.type = 0;
this.condition = ncondition;
} else if (condPattern.equals("-f")) {
ResourceCondition ncondition = new ResourceCondition();
ncondition.type = 1;
this.condition = ncondition;
} else if (condPattern.equals("-s")) {
ResourceCondition ncondition = new ResourceCondition();
ncondition.type = 2;
this.condition = ncondition;
} else {
PatternCondition ncondition = new PatternCondition();
int flags = 0;
if (isNocase()) {
flags |= Pattern.CASE_INSENSITIVE;
}
ncondition.pattern = Pattern.compile(condPattern, flags);
this.condition = ncondition;
}
}
public Matcher getMatcher() {
if (condition instanceof PatternCondition) {
return ((PatternCondition) condition).getMatcher();
}
return null;
}
/**
* String representation.
*/
@Override
public String toString() {
return "RewriteCond " + testString + " " + condPattern
+ ((flagsString != null) ? (" " + flagsString) : "");
}
protected boolean positive = true;
protected Substitution test = null;
protected Condition condition = null;
/**
* This makes the test case-insensitive, i.e., there is no difference between
* 'A-Z' and 'a-z' both in the expanded TestString and the CondPattern. This
* flag is effective only for comparisons between TestString and CondPattern.
* It has no effect on filesystem and subrequest checks.
*/
public boolean nocase = false;
/**
* Use this to combine rule conditions with a local OR instead of the implicit AND.
*/
public boolean ornext = false;
/**
* Evaluate the condition based on the context
*
* @param rule corresponding matched rule
* @param cond last matched condition
* @param resolver Property resolver
* @return <code>true</code> if the condition matches
*/
public boolean evaluate(Matcher rule, Matcher cond, Resolver resolver) {
String value = test.evaluate(rule, cond, resolver);
if (positive) {
return condition.evaluate(value, resolver);
} else {
return !condition.evaluate(value, resolver);
}
}
public boolean isNocase() {
return nocase;
}
public void setNocase(boolean nocase) {
this.nocase = nocase;
}
public boolean isOrnext() {
return ornext;
}
public void setOrnext(boolean ornext) {
this.ornext = ornext;
}
public boolean isPositive() {
return positive;
}
public void setPositive(boolean positive) {
this.positive = positive;
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
public interface RewriteMap {
public String setParameters(String params);
public String lookup(String key);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,347 @@
/*
* 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.List;
import java.util.Map;
import java.util.regex.Matcher;
import org.apache.catalina.util.URLEncoder;
public class Substitution {
public abstract class SubstitutionElement {
public abstract String evaluate(Matcher rule, Matcher cond, Resolver resolver);
}
public class StaticElement extends SubstitutionElement {
public String value;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return value;
}
}
public class RewriteRuleBackReferenceElement extends SubstitutionElement {
public int n;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
String result = rule.group(n);
if (result == null) {
result = "";
}
if (escapeBackReferences) {
// Note: This should be consistent with the way httpd behaves.
// We might want to consider providing a dedicated decoder
// with an option to add additional safe characters to
// provide users with more flexibility
return URLEncoder.DEFAULT.encode(result, resolver.getUriCharset());
} else {
return result;
}
}
}
public class RewriteCondBackReferenceElement extends SubstitutionElement {
public int n;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return (cond.group(n) == null ? "" : cond.group(n));
}
}
public class ServerVariableElement extends SubstitutionElement {
public String key;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return resolver.resolve(key);
}
}
public class ServerVariableEnvElement extends SubstitutionElement {
public String key;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return resolver.resolveEnv(key);
}
}
public class ServerVariableSslElement extends SubstitutionElement {
public String key;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return resolver.resolveSsl(key);
}
}
public class ServerVariableHttpElement extends SubstitutionElement {
public String key;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return resolver.resolveHttp(key);
}
}
public class MapElement extends SubstitutionElement {
public RewriteMap map = null;
public SubstitutionElement[] defaultValue = null;
public SubstitutionElement[] key = null;
@Override
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
String result = map.lookup(evaluateSubstitution(key, rule, cond, resolver));
if (result == null && defaultValue != null) {
result = evaluateSubstitution(defaultValue, rule, cond, resolver);
}
return result;
}
}
protected SubstitutionElement[] elements = null;
protected String sub = null;
public String getSub() { return sub; }
public void setSub(String sub) { this.sub = sub; }
private boolean escapeBackReferences;
void setEscapeBackReferences(boolean escapeBackReferences) {
this.escapeBackReferences = escapeBackReferences;
}
public void parse(Map<String, RewriteMap> maps) {
this.elements = parseSubtitution(sub, maps);
}
private SubstitutionElement[] parseSubtitution(String sub, Map<String, RewriteMap> maps) {
List<SubstitutionElement> elements = new ArrayList<>();
int pos = 0;
int percentPos = 0;
int dollarPos = 0;
int backslashPos = 0;
while (pos < sub.length()) {
percentPos = sub.indexOf('%', pos);
dollarPos = sub.indexOf('$', pos);
backslashPos = sub.indexOf('\\', pos);
if (percentPos == -1 && dollarPos == -1 && backslashPos == -1) {
// Static text
StaticElement newElement = new StaticElement();
newElement.value = sub.substring(pos, sub.length());
pos = sub.length();
elements.add(newElement);
} else if (isFirstPos(backslashPos, dollarPos, percentPos)) {
if (backslashPos + 1 == sub.length()) {
throw new IllegalArgumentException(sub);
}
StaticElement newElement = new StaticElement();
newElement.value = sub.substring(pos, backslashPos) + sub.substring(backslashPos + 1, backslashPos + 2);
pos = backslashPos + 2;
elements.add(newElement);
} else if (isFirstPos(dollarPos, percentPos)) {
// $: back reference to rule or map lookup
if (dollarPos + 1 == sub.length()) {
throw new IllegalArgumentException(sub);
}
if (pos < dollarPos) {
// Static text
StaticElement newElement = new StaticElement();
newElement.value = sub.substring(pos, dollarPos);
pos = dollarPos;
elements.add(newElement);
}
if (Character.isDigit(sub.charAt(dollarPos + 1))) {
// $: back reference to rule
RewriteRuleBackReferenceElement newElement = new RewriteRuleBackReferenceElement();
newElement.n = Character.digit(sub.charAt(dollarPos + 1), 10);
pos = dollarPos + 2;
elements.add(newElement);
} else if (sub.charAt(dollarPos + 1) == '{') {
// $: map lookup as ${mapname:key|default}
MapElement newElement = new MapElement();
int open = sub.indexOf('{', dollarPos);
int colon = findMatchingColonOrBar(true, sub, open);
int def = findMatchingColonOrBar(false, sub, open);
int close = findMatchingBrace(sub, open);
if (!(-1 < open && open < colon && colon < close)) {
throw new IllegalArgumentException(sub);
}
newElement.map = maps.get(sub.substring(open + 1, colon));
if (newElement.map == null) {
throw new IllegalArgumentException(sub + ": No map: " + sub.substring(open + 1, colon));
}
String key = null;
String defaultValue = null;
if (def > -1) {
if (!(colon < def && def < close)) {
throw new IllegalArgumentException(sub);
}
key = sub.substring(colon + 1, def);
defaultValue = sub.substring(def + 1, close);
} else {
key = sub.substring(colon + 1, close);
}
newElement.key = parseSubtitution(key, maps);
if (defaultValue != null) {
newElement.defaultValue = parseSubtitution(defaultValue, maps);
}
pos = close + 1;
elements.add(newElement);
} else {
throw new IllegalArgumentException(sub + ": missing digit or curly brace.");
}
} else {
// %: back reference to condition or server variable
if (percentPos + 1 == sub.length()) {
throw new IllegalArgumentException(sub);
}
if (pos < percentPos) {
// Static text
StaticElement newElement = new StaticElement();
newElement.value = sub.substring(pos, percentPos);
pos = percentPos;
elements.add(newElement);
}
if (Character.isDigit(sub.charAt(percentPos + 1))) {
// %: back reference to condition
RewriteCondBackReferenceElement newElement = new RewriteCondBackReferenceElement();
newElement.n = Character.digit(sub.charAt(percentPos + 1), 10);
pos = percentPos + 2;
elements.add(newElement);
} else if (sub.charAt(percentPos + 1) == '{') {
// %: server variable as %{variable}
SubstitutionElement newElement = null;
int open = sub.indexOf('{', percentPos);
int colon = findMatchingColonOrBar(true, sub, open);
int close = findMatchingBrace(sub, open);
if (!(-1 < open && open < close)) {
throw new IllegalArgumentException(sub);
}
if (colon > -1 && open < colon && colon < close) {
String type = sub.substring(open + 1, colon);
if (type.equals("ENV")) {
newElement = new ServerVariableEnvElement();
((ServerVariableEnvElement) newElement).key = sub.substring(colon + 1, close);
} else if (type.equals("SSL")) {
newElement = new ServerVariableSslElement();
((ServerVariableSslElement) newElement).key = sub.substring(colon + 1, close);
} else if (type.equals("HTTP")) {
newElement = new ServerVariableHttpElement();
((ServerVariableHttpElement) newElement).key = sub.substring(colon + 1, close);
} else {
throw new IllegalArgumentException(sub + ": Bad type: " + type);
}
} else {
newElement = new ServerVariableElement();
((ServerVariableElement) newElement).key = sub.substring(open + 1, close);
}
pos = close + 1;
elements.add(newElement);
} else {
throw new IllegalArgumentException(sub + ": missing digit or curly brace.");
}
}
}
return elements.toArray(new SubstitutionElement[0]);
}
private static int findMatchingBrace(String sub, int start) {
int nesting = 1;
for (int i = start + 1; i < sub.length(); i++) {
char c = sub.charAt(i);
if (c == '{') {
char previousChar = sub.charAt(i-1);
if (previousChar == '$' || previousChar == '%') {
nesting++;
}
} else if (c == '}') {
nesting--;
if (nesting == 0) {
return i;
}
}
}
return -1;
}
private static int findMatchingColonOrBar(boolean colon, String sub, int start) {
int nesting = 0;
for (int i = start + 1; i < sub.length(); i++) {
char c = sub.charAt(i);
if (c == '{') {
char previousChar = sub.charAt(i-1);
if (previousChar == '$' || previousChar == '%') {
nesting++;
}
} else if (c == '}') {
nesting--;
} else if (colon ? c == ':' : c =='|') {
if (nesting == 0) {
return i;
}
}
}
return -1;
}
/**
* Evaluate the substitution based on the context.
* @param rule corresponding matched rule
* @param cond last matched condition
* @param resolver The property resolver
* @return The substitution result
*/
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
return evaluateSubstitution(elements, rule, cond, resolver);
}
private String evaluateSubstitution(SubstitutionElement[] elements, Matcher rule, Matcher cond, Resolver resolver) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < elements.length; i++) {
buf.append(elements[i].evaluate(rule, cond, resolver));
}
return buf.toString();
}
/**
* Checks whether the first int is non negative and smaller than any non negative other int
* given with {@code others}.
*
* @param testPos
* integer to test against
* @param others
* list of integers that are paired against {@code testPos}. Any
* negative integer will be ignored.
* @return {@code true} if {@code testPos} is not negative and is less then any given other
* integer, {@code false} otherwise
*/
private boolean isFirstPos(int testPos, int... others) {
if (testPos < 0) {
return false;
}
for (int other : others) {
if (other >= 0 && other < testPos) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<mbeans-descriptors>
<mbean name="RewriteValve"
description="A URL rewrite valve"
domain="Catalina"
group="Valve"
type="org.apache.catalina.valves.rewrite.RewriteValve">
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="configuration"
description="Rewrite configuration"
type="java.lang.String" />
</mbean>
</mbeans-descriptors>