001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.protocol.http.modifier;
020:
021: import java.io.Serializable;
022:
023: import org.apache.jmeter.processor.PreProcessor;
024: import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
025: import org.apache.jmeter.protocol.http.util.HTTPArgument;
026: import org.apache.jmeter.samplers.SampleResult;
027: import org.apache.jmeter.samplers.Sampler;
028: import org.apache.jmeter.testelement.AbstractTestElement;
029: import org.apache.jmeter.testelement.property.BooleanProperty;
030: import org.apache.jmeter.threads.JMeterContext;
031: import org.apache.jmeter.util.JMeterUtils;
032: import org.apache.oro.text.regex.MatchResult;
033: import org.apache.oro.text.regex.Pattern;
034: import org.apache.oro.text.regex.Perl5Compiler;
035: import org.apache.oro.text.regex.Perl5Matcher;
036:
037: //For unit tests, @see TestURLRewritingModifier
038:
039: /**
040: * @author mstover
041: * @author <a href="mailto:jsalvata@apache.org">Jordi Salvat i Alabart</a>
042: */
043:
044: public class URLRewritingModifier extends AbstractTestElement implements
045: Serializable, PreProcessor {
046:
047: private static final String SEMI_COLON = ";"; // $NON-NLS-1$
048:
049: private transient Pattern pathExtensionEqualsQuestionmarkRegexp;
050:
051: private transient Pattern pathExtensionEqualsNoQuestionmarkRegexp;
052:
053: private transient Pattern parameterRegexp;
054:
055: private transient Pattern pathExtensionNoEqualsQuestionmarkRegexp;
056:
057: private transient Pattern pathExtensionNoEqualsNoQuestionmarkRegexp;
058:
059: // transient Perl5Compiler compiler = new Perl5Compiler();
060: private final static String ARGUMENT_NAME = "argument_name"; // $NON-NLS-1$
061:
062: private final static String PATH_EXTENSION = "path_extension"; // $NON-NLS-1$
063:
064: private final static String PATH_EXTENSION_NO_EQUALS = "path_extension_no_equals"; // $NON-NLS-1$
065:
066: private final static String PATH_EXTENSION_NO_QUESTIONMARK = "path_extension_no_questionmark"; // $NON-NLS-1$
067:
068: private final static String SHOULD_CACHE = "cache_value"; // $NON-NLS-1$
069:
070: // PreProcessors are cloned per-thread, so this will be saved per-thread
071: private transient String savedValue = ""; // $NON-NLS-1$
072:
073: public void process() {
074: JMeterContext ctx = getThreadContext();
075: Sampler sampler = ctx.getCurrentSampler();
076: if (!(sampler instanceof HTTPSamplerBase)) {// Ignore non-HTTP samplers
077: return;
078: }
079: SampleResult responseText = ctx.getPreviousResult();
080: if (responseText == null) {
081: return;
082: }
083: initRegex(getArgumentName());
084: String text = new String(responseText.getResponseData());
085: Perl5Matcher matcher = JMeterUtils.getMatcher();
086: String value = "";
087: if (isPathExtension() && isPathExtensionNoEquals()
088: && isPathExtensionNoQuestionmark()) {
089: if (matcher.contains(text,
090: pathExtensionNoEqualsNoQuestionmarkRegexp)) {
091: MatchResult result = matcher.getMatch();
092: value = result.group(1);
093: }
094: } else if (isPathExtension() && isPathExtensionNoEquals()) // && !
095: // isPathExtensionNoQuestionmark
096: {
097: if (matcher.contains(text,
098: pathExtensionNoEqualsQuestionmarkRegexp)) {
099: MatchResult result = matcher.getMatch();
100: value = result.group(1);
101: }
102: } else if (isPathExtension() && isPathExtensionNoQuestionmark()) // && !
103: // isPathExtensionNoEquals
104: {
105: if (matcher.contains(text,
106: pathExtensionEqualsNoQuestionmarkRegexp)) {
107: MatchResult result = matcher.getMatch();
108: value = result.group(1);
109: }
110: } else if (isPathExtension()) // && ! isPathExtensionNoEquals && !
111: // isPathExtensionNoQuestionmark
112: {
113: if (matcher.contains(text,
114: pathExtensionEqualsQuestionmarkRegexp)) {
115: MatchResult result = matcher.getMatch();
116: value = result.group(1);
117: }
118: } else // if ! isPathExtension()
119: {
120: if (matcher.contains(text, parameterRegexp)) {
121: MatchResult result = matcher.getMatch();
122: for (int i = 1; i < result.groups(); i++) {
123: value = result.group(i);
124: if (value != null)
125: break;
126: }
127: }
128: }
129:
130: // Bug 15025 - save session value across samplers
131: if (shouldCache()) {
132: if (value == null || value.length() == 0) {
133: value = savedValue;
134: } else {
135: savedValue = value;
136: }
137: }
138: modify((HTTPSamplerBase) sampler, value);
139: }
140:
141: private void modify(HTTPSamplerBase sampler, String value) {
142: if (isPathExtension()) {
143: if (isPathExtensionNoEquals()) {
144: sampler.setPath(sampler.getPath() + SEMI_COLON
145: + getArgumentName() + value); // $NON-NLS-1$
146: } else {
147: sampler.setPath(sampler.getPath() + SEMI_COLON
148: + getArgumentName() + "=" + value); // $NON-NLS-1$ // $NON-NLS-2$
149: }
150: } else {
151: sampler.getArguments().removeArgument(getArgumentName());
152: sampler.getArguments().addArgument(
153: new HTTPArgument(getArgumentName(), value, true));
154: }
155: }
156:
157: public void setArgumentName(String argName) {
158: setProperty(ARGUMENT_NAME, argName);
159: }
160:
161: private void initRegex(String argName) {
162: String quotedArg = Perl5Compiler.quotemeta(argName);// Don't get tripped up by RE chars in the arg name
163: pathExtensionEqualsQuestionmarkRegexp = JMeterUtils
164: .getPatternCache().getPattern(
165: SEMI_COLON + quotedArg
166: + "=([^\"'>&\\s;]*)[&\\s\"'>;]?$?", // $NON-NLS-1$
167: Perl5Compiler.MULTILINE_MASK
168: | Perl5Compiler.READ_ONLY_MASK);
169:
170: pathExtensionEqualsNoQuestionmarkRegexp = JMeterUtils
171: .getPatternCache().getPattern(
172: SEMI_COLON + quotedArg
173: + "=([^\"'>&\\s;?]*)[&\\s\"'>;?]?$?", // $NON-NLS-1$
174: Perl5Compiler.MULTILINE_MASK
175: | Perl5Compiler.READ_ONLY_MASK);
176:
177: pathExtensionNoEqualsQuestionmarkRegexp = JMeterUtils
178: .getPatternCache().getPattern(
179: SEMI_COLON + quotedArg
180: + "([^\"'>&\\s;]*)[&\\s\"'>;]?$?", // $NON-NLS-1$
181: Perl5Compiler.MULTILINE_MASK
182: | Perl5Compiler.READ_ONLY_MASK);
183:
184: pathExtensionNoEqualsNoQuestionmarkRegexp = JMeterUtils
185: .getPatternCache().getPattern(
186: SEMI_COLON + quotedArg
187: + "([^\"'>&\\s;?]*)[&\\s\"'>;?]?$?", // $NON-NLS-1$
188: Perl5Compiler.MULTILINE_MASK
189: | Perl5Compiler.READ_ONLY_MASK);
190:
191: parameterRegexp = JMeterUtils.getPatternCache().getPattern(
192: // ;sessionid=value
193: "[;\\?&]"
194: + quotedArg
195: + "=([^\"'>&\\s;]*)[&\\s\"'>;]?$?"
196: + // $NON-NLS-1$
197:
198: // name="sessionid" value="value"
199: "|\\s[Nn][Aa][Mm][Ee]\\s*=\\s*[\"']"
200: + quotedArg + "[\"']" + "[^>]*" // $NON-NLS-1$
201: + "\\s[vV][Aa][Ll][Uu][Ee]\\s*=\\s*[\"']" // $NON-NLS-1$
202: + "([^\"']*)" + "[\"']" // $NON-NLS-1$
203:
204: // value="value" name="sessionid"
205: + "|\\s[vV][Aa][Ll][Uu][Ee]\\s*=\\s*[\"']" // $NON-NLS-1$
206: + "([^\"']*)" + "[\"']" + "[^>]*" // $NON-NLS-1$ // $NON-NLS-2$ // $NON-NLS-3$
207: + "\\s[Nn][Aa][Mm][Ee]\\s*=\\s*[\"']" // $NON-NLS-1$
208: + quotedArg + "[\"']", // $NON-NLS-1$
209: Perl5Compiler.MULTILINE_MASK
210: | Perl5Compiler.READ_ONLY_MASK);
211: // NOTE: the handling of simple- vs. double-quotes could be formally
212: // more accurate, but I can't imagine a session id containing
213: // either, so we should be OK. The whole set of expressions is a
214: // quick hack anyway, so who cares.
215: }
216:
217: public String getArgumentName() {
218: return getPropertyAsString(ARGUMENT_NAME);
219: }
220:
221: public void setPathExtension(boolean pathExt) {
222: setProperty(new BooleanProperty(PATH_EXTENSION, pathExt));
223: }
224:
225: public void setPathExtensionNoEquals(boolean pathExtNoEquals) {
226: setProperty(new BooleanProperty(PATH_EXTENSION_NO_EQUALS,
227: pathExtNoEquals));
228: }
229:
230: public void setPathExtensionNoQuestionmark(
231: boolean pathExtNoQuestionmark) {
232: setProperty(new BooleanProperty(PATH_EXTENSION_NO_QUESTIONMARK,
233: pathExtNoQuestionmark));
234: }
235:
236: public void setShouldCache(boolean b) {
237: setProperty(new BooleanProperty(SHOULD_CACHE, b));
238: }
239:
240: public boolean isPathExtension() {
241: return getPropertyAsBoolean(PATH_EXTENSION);
242: }
243:
244: public boolean isPathExtensionNoEquals() {
245: return getPropertyAsBoolean(PATH_EXTENSION_NO_EQUALS);
246: }
247:
248: public boolean isPathExtensionNoQuestionmark() {
249: return getPropertyAsBoolean(PATH_EXTENSION_NO_QUESTIONMARK);
250: }
251:
252: public boolean shouldCache() {
253: return getPropertyAsBoolean(SHOULD_CACHE, true);
254: }
255:
256: }
|