Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/jmeter.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,8 @@ cookies=cookies

# Ability to switch out the old Oro Regex implementation with the JDK built-in implementation
# Any value different to 'oro' will disable the Oro implementation and enable the JDK based.
#jmeter.regex.engine=oro
# Usage of Oro is deprecated now in JMeter >=6.0
jmeter.regex.engine=java

# We assist the JDK based Regex implementation by caching Pattern objects. The size of the
# cache can be set with this setting. It can be disabled by setting it to '0'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
Expand All @@ -33,7 +34,8 @@
import com.google.auto.service.AutoService;

/**
* Escape ORO meta characters
* Escape Regex meta characters
*
* @since 2.9
*/
@AutoService(Function.class)
Expand All @@ -44,6 +46,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {

private static final String KEY = "__escapeOroRegexpChars"; //$NON-NLS-1$

private final boolean USE_JAVA_REGEX;

static {
desc.add(JMeterUtils.getResString("value_to_quote_meta")); //$NON-NLS-1$
desc.add(JMeterUtils.getResString("function_name_paropt")); //$NON-NLS-1$
Expand All @@ -62,6 +66,8 @@ public class EscapeOroRegexpChars extends AbstractFunction {
*/
public EscapeOroRegexpChars() {
super();
USE_JAVA_REGEX = !JMeterUtils.getPropDefault(
"jmeter.regex.engine", "oro").equalsIgnoreCase("oro");
}

/** {@inheritDoc} */
Expand All @@ -76,7 +82,7 @@ public String execute(SampleResult previousResult, Sampler currentSampler)
varName = values[PARAM_NAME - 1].execute().trim();
}

String escapedValue = Perl5Compiler.quotemeta(valueToEscape);
String escapedValue = USE_JAVA_REGEX ? Pattern.quote(valueToEscape) : Perl5Compiler.quotemeta(valueToEscape);

if (!varName.isEmpty()) {
JMeterVariables vars = getVariables();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,46 @@
package org.apache.jmeter.functions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Stream;

import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.gui.action.AbstractAction;
import org.apache.jmeter.junit.JMeterTestCase;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

/**
* Test the function __escapeOroRegexpCars
*
* To prepare for removal of Oro, we changed the behavior of the function
* to use Oro or JDKs internal Regex implementation based on the JMeter property
* {@code jmeter.regex.engine}
*
* Those two implementations have a slightly different way of escaping, so
* test both here, until we got rid of Oro.
*/
public class TestEscapeOroRegexpChars extends JMeterTestCase {

private AbstractFunction function;
private SampleResult result;
private Collection<CompoundVariable> params;
private JMeterVariables vars;
private JMeterContext jmctx;

@BeforeEach
void setUp() {
function = new EscapeOroRegexpChars();
result = new SampleResult();
jmctx = JMeterContextService.getContext();
JMeterContext jmctx = JMeterContextService.getContext();
String data = "The quick brown fox";
result.setResponseData(data, null);
vars = new JMeterVariables();
Expand All @@ -53,48 +68,53 @@ void setUp() {

@Test
void testParameterCount() throws Exception {
checkInvalidParameterCounts(function, 1, 2);
checkInvalidParameterCounts(new EscapeOroRegexpChars(), 1, 2);
}

@Test
void testNOEscape() throws Exception {
params.add(new CompoundVariable("toto1titi"));
function.setParameters(params);
String ret = function.execute(result, null);
Assertions.assertEquals("toto1titi", ret);
static Collection<Arguments> functionAndParams() {
var testValuesPerImplementation = Map.of(
"oro", Map.of(
"toto1titi", "toto1titi",
"toto titi", "toto\\ titi",
"toto(.+?)titi", "toto\\(\\.\\+\\?\\)titi",
"[^\"].+?","\\[\\^\\\"\\]\\.\\+\\?"
),
"java", Map.of(
"toto1titi", "\\Qtoto1titi\\E",
"toto titi", "\\Qtoto titi\\E",
"toto(.+?)titi", "\\Qtoto(.+?)titi\\E",
"[^\"].+?", "\\Q[^\"].+?\\E"
)
);
Collection<Arguments> args = new ArrayList<>();
for (var implementation: testValuesPerImplementation.entrySet()) {
JMeterUtils.setProperty("jmeter.regex.engine", implementation.getKey());
AbstractFunction function = new EscapeOroRegexpChars();
for (var testValues: implementation.getValue().entrySet()) {
args.add(Arguments.of(function, testValues.getKey(), testValues.getValue()));
}
}
return args;
}

@Test
void testEscapeSpace() throws Exception {
params.add(new CompoundVariable("toto1 titi"));
@ParameterizedTest
@MethodSource("functionAndParams")
void testEscaping(AbstractFunction function, String value, String expected) throws Exception {
params.add(new CompoundVariable(value));
function.setParameters(params);
String ret = function.execute(result, null);
Assertions.assertEquals("toto1\\ titi", ret);
Assertions.assertEquals(expected, ret);
}

@Test
void testEscape() throws Exception {
params.add(new CompoundVariable("toto(.+?)titi"));
function.setParameters(params);
String ret = function.execute(result, null);
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
}

@Test
void testEscapeWithVars() throws Exception {
params.add(new CompoundVariable("toto(.+?)titi"));
@ParameterizedTest
@MethodSource("functionAndParams")
void testEscapingWithVar(AbstractFunction function, String value, String expected) throws Exception {
params.add(new CompoundVariable(value));
params.add(new CompoundVariable("exportedVar"));
function.setParameters(params);
String ret = function.execute(result, null);
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", ret);
Assertions.assertEquals("toto\\(\\.\\+\\?\\)titi", vars.get("exportedVar"));
Assertions.assertEquals(expected, ret);
Assertions.assertEquals(expected, vars.get("exportedVar"));
}

@Test
void testEscape2() throws Exception {
params.add(new CompoundVariable("[^\"].+?"));
function.setParameters(params);
String ret = function.execute(result, null);
Assertions.assertEquals("\\[\\^\\\"\\]\\.\\+\\?", ret);
}
}
1 change: 1 addition & 0 deletions xdocs/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Summary
<li><issue>6352</issue> Calculate delays in Open Model Thread Group and Precise Throughput
Timer relative to start of Thread Group instead of the start of the test.</li>
<li><issue>6357</issue><pr>6358</pr> Ensure writable directories when copying template files while report generation.</li>
<li><issue>6645</issue><pr>6661</pr>Deprecate usage of Oro Regex implementation and switch default Regex implementation to JDK built-in</li>
</ul>

<h3>HTTP Samplers and Test Script Recorder</h3>
Expand Down
14 changes: 5 additions & 9 deletions xdocs/usermanual/component_reference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4471,11 +4471,11 @@ GUI that they can use while developing new JMeter components.</p>
The pattern strings are:
</p>
<ul>
<li><code>Contains</code>, <code>Matches</code>: Perl5-style regular expressions</li>
<li><code>Contains</code>, <code>Matches</code>: Java regular expressions</li>
<li><code>Equals</code>, <code>Substring</code>: plain text, case-sensitive</li>
</ul>
<p>
A summary of the pattern matching characters can be found at <a href="http://jakarta.apache.org/oro/api/org/apache/oro/text/regex/package-summary.html">ORO Perl5 regular expressions.</a>
A summary of the pattern matching characters can be found at <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html">Java regular expressions.</a>
</p>
<p>You can also choose whether the strings will be expected
to <b>match</b> the entire response, or if the response is only expected to <b>contain</b> the
Expand Down Expand Up @@ -6902,7 +6902,7 @@ To disable the redirect detection, set the property <code>proxy.redirect.disabli
</p>

<h4>Includes and Excludes</h4>
<p>The <b>include and exclude patterns</b> are treated as regular expressions (using Jakarta ORO).
<p>The <b>include and exclude patterns</b> are treated as regular expressions.
They will be matched against the host name, port (actual or implied), path and query (if any) of each browser request.
If the URL you are browsing is <br></br>
"<code>http://localhost/jmeter/index.html?username=xxxx</code>",<br></br>
Expand Down Expand Up @@ -6992,7 +6992,7 @@ place User Defined Variables directly within the HTTP(S) Test Script Recorder to
If you define the variable <code>WEB</code> with the value <code>www</code>, for example,
the string <code>www</code> will be replaced by <code>${WEB}</code> wherever it is found.
To avoid this happening everywhere, set the "<code>Regex Matching</code>" check-box.
This tells the proxy server to treat values as Regexes (using the perl5 compatible regex matchers provided by ORO).</p>
This tells the proxy server to treat values as Regexes.</p>

<p>If "<code>Regex Matching</code>" is selected every variable will be compiled into a perl compatible regex enclosed in
<code>\b(</code> and <code>)\b</code>. That way each match will start and end at a word boundary.</p>
Expand All @@ -7015,13 +7015,9 @@ The parens are necessary, since the normally added boundary characters will prev
<code>$</code> to match.</p>

<p>If you want to match <code>/images</code> at the start of a string only, use the value <code>(^/images)</code>.
Jakarta ORO also supports zero-width look-ahead, so one can match <code>/images/&hellip;</code>
Javas Regex implementation also supports zero-width look-ahead, so one can match <code>/images/&hellip;</code>
but retain the trailing <code>/</code> in the output by using <code>(^/images(?=/))</code>.</p>

<note>
Note that the current version of Jakarta ORO does not support look-behind - i.e. <code>(?&lt;=&hellip;)</code> or <code>(?&lt;!&hellip;)</code>.
</note>

<p>Look out for overlapping matchers. For example the value <code>.*</code> as a regex in a variable named
<code>regex</code> will partly match a previous replaced variable, which will result in something like
<code>${{regex}</code>, which is most probably not the desired result.</p>
Expand Down
7 changes: 4 additions & 3 deletions xdocs/usermanual/functions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Alternatively, just use <code>/</code> instead for the path separator - e.g. <co
<tr><td>String</td><td> <a href="#__char">char</a></td><td>generate Unicode char values from a list of numbers</td><td>2.3.3</td></tr>
<tr><td>String</td><td> <a href="#__changeCase">changeCase</a></td><td>Change case following different modes</td><td>4.0</td></tr>
<tr><td>String</td><td> <a href="#__escapeHtml">escapeHtml</a></td><td>Encode strings using HTML encoding</td><td>2.3.3</td></tr>
<tr><td>String</td><td> <a href="#__escapeOroRegexpChars">escapeOroRegexpChars</a></td><td>quote meta chars used by ORO regular expression</td><td>2.9</td></tr>
<tr><td>String</td><td> <a href="#__escapeOroRegexpChars">escapeOroRegexpChars</a></td><td>quote meta chars used by Java regular expression</td><td>2.9</td></tr>
<tr><td>String</td><td> <a href="#__escapeXml">escapeXml</a></td><td>Encode strings using XMl encoding</td><td>3.2</td></tr>
<tr><td>String</td><td> <a href="#__regexFunction">regexFunction</a></td><td>parse previous response using a regular expression</td><td>1.X</td></tr>
<tr><td>String</td><td> <a href="#__unescape">unescape</a></td><td>Process strings containing Java escapes (e.g. \n &amp; \t)</td><td>2.3.3</td></tr>
Expand Down Expand Up @@ -1530,15 +1530,16 @@ A reference name - <code>refName</code> - for reusing the value created by this
<component index="&sect-num;.5.32" name="__escapeOroRegexpChars">
<description>
<p>
Function which escapes the ORO Regexp meta characters, it is the equivalent of <code>\Q</code> <code>\E</code> in Java Regexp Engine.
Function which escapes the Java Regexp meta characters, it is the equivalent of <code>\Q</code> <code>\E</code> in Java Regexp Engine.
<note>With JMeter 6.0 the quoting changed from Oro to Java internal.</note>
</p>
<p>
For example,<source>${__escapeOroRegexpChars([^&quot;].+?,)}</source>
returns:
<code>\[\^\&quot;\]\.\+\?</code>.
</p>
<p>
Uses Perl5Compiler#quotemeta(String) from ORO.
Uses java.util.regex.Pattern#quote(String) from JDK.
</p>
</description>

Expand Down
4 changes: 2 additions & 2 deletions xdocs/usermanual/properties_reference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1434,8 +1434,8 @@ JMETER-SERVER</source>
<property name="jmeter.regex.engine">
Ability to switch out the old Oro Regex implementation with the JDK built-in implementation.
Any value different to <code>oro</code> will disable the Oro implementation and enable the JDK based.
<note>We intend to switch the default to the JDK based one in a later version of JMeter.</note>
Defaults to: <code>oro</code>
<note>We switched the default to the JDK based one in a JMeter 6.0.</note>
Defaults to: <code>java</code>
</property>
<property name="jmeter.regex.patterncache.size">
We assist the JDK based Regex implementation by caching Pattern objects. The size of the
Expand Down
13 changes: 8 additions & 5 deletions xdocs/usermanual/regular_expressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ a summary of the pattern matching characters</a>
There is also documentation on an older incarnation of the product at
<a href="http://www.savarese.org/oro/docs/OROMatcher/index.html">OROMatcher User's guide</a>, which might prove useful.
</p>
<note>With JMeter version 5.5 the Regex implementation can be switched from Oro to the JDK based one by setting
the JMeter property <code>jmeter.regex.engine</code> to some value different than <code>oro</code>.</note>
<note>With JMeter version 6.0 the Regex implementation switched from Oro to the JDK based one by setting
the JMeter property <code>jmeter.regex.engine</code>. You can switch it back by setting the value to <code>oro</code>.</note>
<p>
The pattern matching is very similar to the pattern matching in Perl.
A full installation of Perl will include plenty of documentation on regular expressions - look for <code>perlrequick</code>,
Expand Down Expand Up @@ -193,7 +193,8 @@ Here is a list of the meta characters and their meaning (please check the ORO do
<note>
Please note that ORO does not support the <code>\Q</code> and <code>\E</code> meta-characters.
[In other RE engines, these can be used to quote a portion of an RE so that the meta-characters stand for themselves.]
You can use function to do the equivalent, see <a href="functions.html#__escapeOroRegexpChars">${__escapeOroRegexpChars(valueToEscape)}</a>.
You can use a function to do the equivalent, see <a href="functions.html#__escapeOroRegexpChars">${__escapeOroRegexpChars(valueToEscape)}</a>.
Only applies, if you switch back to Oro.
</note>
<p>
The following Perl5 extended regular expressions are supported by ORO.
Expand Down Expand Up @@ -222,8 +223,10 @@ of the input, <code>s</code> enables single line treatment of the input, and <co
<subsection name="&sect-num;.5 Placement of modifiers" anchor="placement">
<p>
Modifiers can be placed anywhere in the regex, and apply from that point onwards.
[A bug in ORO means that they cannot be used at the very end of the regex.
However they would have no effect there anyway.]
<note>
A bug in ORO means that they cannot be used at the very end of the regex.
However they would have no effect there anyway. Only applies, when you switch back to Oro
</note>
</p>
<p>
The single-line <code>(?s)</code> and multi-line <code>(?m)</code> modifiers are normally placed at the start of the regex.
Expand Down
Loading