Error with %{query-string} when query string contains $

58 views
Skip to first unread message

Tim

unread,
Nov 7, 2007, 5:33:10 PM11/7/07
to UrlRewrite
I've been trying to capture the user's original request and query
string for constructing links to a page. Is there an easy way to do
this?

Ultimately, I have this in my rule:
<set name="urlrewrite.originalRequestUri">%{request-uri}</set>
<set name="urlrewrite.originalQueryString">%{query-string}</set>

Works like a charm. I can grab those values as request attributes
later.

However... if my request query string contains a dollar ($), I get a
StringIndexOutOfBoundsException. Now I realize that isn't a valid
character in a query string (it should be escaped) but a user can
easily type it into their address bar.

For example:
/whatver?x=$
results in:
java.lang.StringIndexOutOfBoundsException: String index out of range:
3
at java.lang.String.charAt(String.java:687)
at java.util.regex.Matcher.appendReplacement(Matcher.java:711)
at
org.tuckey.web.filters.urlrewrite.VariableReplacer.replace(VariableReplacer.java:
88)

It works just fine if there is no "$" there. I narrowed it down to a
unit test (using junit 4 and jMock to stub the request):

import static org.junit.Assert.assertEquals;

import javax.servlet.http.HttpServletRequest;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
import org.tuckey.web.filters.urlrewrite.VariableReplacer;

public class UrlRewriteVariableReplacerTest {

private Mockery context;
private HttpServletRequest request;

@Before
public void setUp() throws Exception {
context = new Mockery();
request = context.mock(HttpServletRequest.class);
}

@Test
public final void testVariableReplace() {
final String queryString = "x=$";
context.checking(new Expectations() {
{
allowing(request).getQueryString();
will(returnValue(queryString));
}
});

final String result = VariableReplacer.replace("%{query-
string}", request);

assertEquals(queryString, result);
}

}

java.lang.StringIndexOutOfBoundsException: String index out of range:
3
at java.lang.String.charAt(String.java:687)
at java.util.regex.Matcher.appendReplacement(Matcher.java:711)
at
org.tuckey.web.filters.urlrewrite.VariableReplacer.replace(VariableReplacer.java:
88)
at
urlrewritefilter.UrlRewriteVariableReplacerTest.testVariableReplace(UrlRewriteVariableReplacerTest.java:
38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
at java.lang.reflect.Method.invoke(Method.java:597)
at
org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:
99)
at
org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:
81)
at
org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:
34)
at
org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:
75)
at
org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:
45)
at
org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:
66)
at
org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:
35)
at org.junit.internal.runners.TestClassRunner
$1.runUnprotected(TestClassRunner.java:42)
at
org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:
34)
at
org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:
52)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:
38)
at
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:
38)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
460)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
673)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:
386)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:
196)

Am I going about this the wrong way?

Thanks,

Tim

Tim

unread,
Nov 7, 2007, 5:56:50 PM11/7/07
to UrlRewrite

So digging in, the VariableReplace code uses
matcher.appendReplacement(). Unfortunately this can fail with the
replacement value (in my case, the query string) contains "$" (or
"\").

The javadoc for that states:
http://java.sun.com/javase/6/docs/api/java/util/regex/Matcher.html#appendReplacement(java.lang.StringBuffer,%20java.lang.String)
Note that backslashes (\) and dollar signs ($) in the replacement
string may cause the results to be different than if it were being
treated as a literal replacement string. Dollar signs may be treated
as references to captured subsequences as described above, and
backslashes are used to escape literal characters in the replacement
string.

It can be fixed by manually tracking the lastAppendPosition and using
substring() to splice together the result.

Instead of
varMatcher.appendReplacement(sb, varValue);
do
sb.append(subjectOfReplacement.substring(lastAppendPosition,
varMatcher.start()));
sb.append(varValue);
lastAppendPosition = varMatcher.end();

and instead of
varMatcher.appendTail(sb);
do
sb.append(subjectOfReplacement.substring(lastAppendPosition,
subjectOfReplacement.length()));

where "lastAppendPosition" was declared outside of the matcher.find()
while loop.

Tim

Tim

unread,
Dec 4, 2007, 5:23:15 PM12/4/07
to UrlRewrite
BTW I created an issue for this: http://code.google.com/p/urlrewritefilter/issues/detail?id=9
With an attached unit test and patch to fix it.

Tim

On Nov 7, 2:56 pm, Tim <tim.mor...@gmail.com> wrote:
> So digging in, the VariableReplace code uses
> matcher.appendReplacement(). Unfortunately this can fail with the
> replacement value (in my case, the query string) contains "$" (or
> "\").
>
> The javadoc for that states:http://java.sun.com/javase/6/docs/api/java/util/regex/Matcher.html#ap...)
Reply all
Reply to author
Forward
0 new messages