Skip to content

Commit 3f8e054

Browse files
committed
CAMEL-10581: toD should safe split uris to avoid issues with RAW values.
1 parent d16e902 commit 3f8e054

File tree

4 files changed

+165
-1
lines changed

4 files changed

+165
-1
lines changed

camel-core/src/main/java/org/apache/camel/model/ToDynamicDefinition.java

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
2123
import javax.xml.bind.annotation.XmlAccessType;
2224
import javax.xml.bind.annotation.XmlAccessorType;
2325
import javax.xml.bind.annotation.XmlAttribute;
@@ -47,6 +49,9 @@
4749
@XmlRootElement(name = "toD")
4850
@XmlAccessorType(XmlAccessType.FIELD)
4951
public class ToDynamicDefinition extends NoOutputDefinition<ToDynamicDefinition> {
52+
53+
private static final Pattern RAW_PATTERN = Pattern.compile("RAW\\([^\\)]+\\)");
54+
5055
@XmlAttribute @Metadata(required = "true")
5156
private String uri;
5257
@XmlAttribute
@@ -83,7 +88,8 @@ public Processor createProcessor(RouteContext routeContext) throws Exception {
8388

8489
protected Expression createExpression(RouteContext routeContext) {
8590
List<Expression> list = new ArrayList<Expression>();
86-
String[] parts = uri.split("\\+");
91+
92+
String[] parts = safeSplitRaw(uri);
8793
for (String part : parts) {
8894
// the part may have optional language to use, so you can mix languages
8995
String value = ObjectHelper.after(part, "language:");
@@ -195,5 +201,76 @@ public void setIgnoreInvalidEndpoint(Boolean ignoreInvalidEndpoint) {
195201
this.ignoreInvalidEndpoint = ignoreInvalidEndpoint;
196202
}
197203

204+
// Utilities
205+
// -------------------------------------------------------------------------
206+
207+
private static class Pair {
208+
int left;
209+
int right;
210+
Pair(int left, int right) {
211+
this.left = left;
212+
this.right = right;
213+
}
214+
}
215+
216+
private static List<Pair> checkRAW(String s) {
217+
Matcher matcher = RAW_PATTERN.matcher(s);
218+
List<Pair> answer = new ArrayList<Pair>();
219+
// Check all occurrences
220+
while (matcher.find()) {
221+
answer.add(new Pair(matcher.start(), matcher.end() - 1));
222+
}
223+
return answer;
224+
}
225+
226+
private static boolean isRaw(int index, List<Pair>pairs) {
227+
for (Pair pair : pairs) {
228+
if (index < pair.left) {
229+
return false;
230+
} else {
231+
if (index >= pair.left) {
232+
if (index <= pair.right) {
233+
return true;
234+
} else {
235+
continue;
236+
}
237+
}
238+
}
239+
}
240+
return false;
241+
}
242+
243+
/**
244+
* We need to split the string safely for each + sign, but avoid splitting within RAW(...).
245+
*/
246+
private static String[] safeSplitRaw(String s) {
247+
List<String> list = new ArrayList<>();
248+
249+
if (!s.contains("+")) {
250+
// no plus sign so there is only one part, so no need to split
251+
list.add(s);
252+
} else {
253+
// there is a plus sign so we need to split in a safe manner
254+
List<Pair> rawPairs = checkRAW(s);
255+
StringBuilder sb = new StringBuilder();
256+
char chars[] = s.toCharArray();
257+
for (int i = 0; i < chars.length; i++) {
258+
char ch = chars[i];
259+
if (ch != '+' || isRaw(i, rawPairs)) {
260+
sb.append(ch);
261+
} else {
262+
list.add(sb.toString());
263+
sb.setLength(0);
264+
}
265+
}
266+
// any leftover?
267+
if (sb.length() > 0) {
268+
list.add(sb.toString());
269+
sb.setLength(0);
270+
}
271+
}
272+
273+
return list.toArray(new String[list.size()]);
274+
}
198275

199276
}

camel-core/src/main/java/org/apache/camel/util/UnsafeUriCharactersEncoder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ private static List<Pair> checkRAW(String s) {
9999
List<Pair> answer = new ArrayList<Pair>();
100100
// Check all occurrences
101101
while (matcher.find()) {
102+
// TODO: should likely be matcher.end() - 1
102103
answer.add(new Pair(matcher.start(), matcher.end()));
103104
}
104105
return answer;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.processor;
18+
19+
import org.apache.camel.ContextTestSupport;
20+
import org.apache.camel.builder.RouteBuilder;
21+
22+
public class ToDynamicRawAndXPathTest extends ContextTestSupport {
23+
24+
public void testToDynamicRawAndPlus() throws Exception {
25+
getMockEndpoint("mock:RAW(se+ret)foo").expectedBodiesReceived("<order uri=\"foo\"/>");
26+
getMockEndpoint("mock:RAW(se+ret)bar").expectedBodiesReceived("<order uri=\"bar\"/>");
27+
28+
template.sendBody("direct:start", "<order uri=\"foo\"/>");
29+
template.sendBody("direct:start", "<order uri=\"bar\"/>");
30+
31+
assertMockEndpointsSatisfied();
32+
}
33+
34+
@Override
35+
protected RouteBuilder createRouteBuilder() throws Exception {
36+
return new RouteBuilder() {
37+
@Override
38+
public void configure() throws Exception {
39+
from("direct:start")
40+
.toD("mock:RAW(se+ret)+language:xpath:/order/@uri");
41+
}
42+
};
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.processor;
18+
19+
import org.apache.camel.ContextTestSupport;
20+
import org.apache.camel.builder.RouteBuilder;
21+
22+
public class ToDynamicRawTest extends ContextTestSupport {
23+
24+
public void testToDynamicRaw() throws Exception {
25+
getMockEndpoint("mock:RAW(se+ret)").expectedBodiesReceived("Hello Camel");
26+
27+
template.sendBody("direct:start", "Hello Camel");
28+
29+
assertMockEndpointsSatisfied();
30+
}
31+
32+
@Override
33+
protected RouteBuilder createRouteBuilder() throws Exception {
34+
return new RouteBuilder() {
35+
@Override
36+
public void configure() throws Exception {
37+
from("direct:start")
38+
.toD("mock:RAW(se+ret)");
39+
}
40+
};
41+
}
42+
}

0 commit comments

Comments
 (0)