You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ts...@apache.org on 2019/01/17 08:37:15 UTC
[camel] branch master updated: CAMEL-12982: Support RAW{} syntax in
URISupport
This is an automated email from the ASF dual-hosted git repository.
tsato pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new 2970e97 CAMEL-12982: Support RAW{} syntax in URISupport
2970e97 is described below
commit 2970e970b66c7af8462c9053032a40e40b420b1f
Author: Tadayoshi Sato <sa...@gmail.com>
AuthorDate: Thu Jan 17 12:34:03 2019 +0900
CAMEL-12982: Support RAW{} syntax in URISupport
---
.../org/apache/camel/reifier/ToDynamicReifier.java | 46 +---
.../org/apache/camel/runtimecatalog/impl/Pair.java | 60 +++++
.../camel/runtimecatalog/impl/URISupport.java | 119 +++++++--
.../impl/UnsafeUriCharactersEncoder.java | 46 +---
.../org/apache/camel/support/DefaultComponent.java | 5 +-
.../issues/EndpointWithRawUriParameterTest.java | 16 +-
.../src/main/java/org/apache/camel/util/Pair.java | 60 +++++
.../java/org/apache/camel/util/URIScanner.java | 270 +++++++++++++++++++++
.../java/org/apache/camel/util/URISupport.java | 256 +++++++------------
.../camel/util/UnsafeUriCharactersEncoder.java | 46 +---
.../java/org/apache/camel/util/URISupportTest.java | 100 +++++++-
11 files changed, 701 insertions(+), 323 deletions(-)
diff --git a/camel-core/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java b/camel-core/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
index e9513a1..8ae929b 100644
--- a/camel-core/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
+++ b/camel-core/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
@@ -18,8 +18,6 @@ package org.apache.camel.reifier;
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.apache.camel.Expression;
import org.apache.camel.NoSuchLanguageException;
@@ -30,12 +28,12 @@ import org.apache.camel.model.ToDynamicDefinition;
import org.apache.camel.processor.SendDynamicProcessor;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.RouteContext;
+import org.apache.camel.util.Pair;
import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorReifier<T> {
- private static final Pattern RAW_PATTERN = Pattern.compile("RAW\\([^\\)]+\\)");
-
ToDynamicReifier(ProcessorDefinition<?> definition) {
super((T) definition);
}
@@ -101,42 +99,6 @@ class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorReifier<T
// Utilities
// -------------------------------------------------------------------------
- private static class Pair {
- int left;
- int right;
- Pair(int left, int right) {
- this.left = left;
- this.right = right;
- }
- }
-
- private static List<Pair> checkRAW(String s) {
- Matcher matcher = RAW_PATTERN.matcher(s);
- List<Pair> answer = new ArrayList<>();
- // Check all occurrences
- while (matcher.find()) {
- answer.add(new Pair(matcher.start(), matcher.end() - 1));
- }
- return answer;
- }
-
- private static boolean isRaw(int index, List<Pair>pairs) {
- for (Pair pair : pairs) {
- if (index < pair.left) {
- return false;
- } else {
- if (index >= pair.left) {
- if (index <= pair.right) {
- return true;
- } else {
- continue;
- }
- }
- }
- }
- return false;
- }
-
/**
* We need to split the string safely for each + sign, but avoid splitting within RAW(...).
*/
@@ -148,12 +110,12 @@ class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorReifier<T
list.add(s);
} else {
// there is a plus sign so we need to split in a safe manner
- List<Pair> rawPairs = checkRAW(s);
+ List<Pair<Integer>> rawPairs = URISupport.scanRaw(s);
StringBuilder sb = new StringBuilder();
char chars[] = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
char ch = chars[i];
- if (ch != '+' || isRaw(i, rawPairs)) {
+ if (ch != '+' || URISupport.isRaw(i, rawPairs)) {
sb.append(ch);
} else {
list.add(sb.toString());
diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/Pair.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/Pair.java
new file mode 100644
index 0000000..d2060d7
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/Pair.java
@@ -0,0 +1,60 @@
+/**
+ * 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.camel.runtimecatalog.impl;
+
+import java.util.Objects;
+
+/**
+ * Copied from org.apache.camel.util.Pair
+ */
+public class Pair<T> {
+
+ private T left;
+ private T right;
+
+ public Pair(T left, T right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public T getLeft() {
+ return left;
+ }
+
+ public T getRight() {
+ return right;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Pair<?> that = (Pair<?>) o;
+ return Objects.equals(left, that.left) &&
+ Objects.equals(right, that.right);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(left, right);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + left + ", " + right + ")";
+ }
+}
diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/URISupport.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/URISupport.java
index 73f05a2..12087ee 100644
--- a/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/URISupport.java
+++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/URISupport.java
@@ -26,14 +26,16 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.BiConsumer;
/**
* Copied from org.apache.camel.util.URISupport
*/
public final class URISupport {
- public static final String RAW_TOKEN_START = "RAW(";
- public static final String RAW_TOKEN_END = ")";
+ public static final String RAW_TOKEN_PREFIX = "RAW";
+ public static final char[] RAW_TOKEN_START = { '(', '{' };
+ public static final char[] RAW_TOKEN_END = { ')', '}' };
private static final String CHARSET = "UTF-8";
@@ -155,17 +157,17 @@ public final class URISupport {
* @see #RAW_TOKEN_END
*/
public static Map<String, Object> parseQuery(String uri, boolean useRaw) throws URISyntaxException {
- // must check for trailing & as the uri.split("&") will ignore those
- if (uri != null && uri.endsWith("&")) {
- throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. "
- + "Check the uri and remove the trailing & marker.");
- }
-
if (isEmpty(uri)) {
// return an empty map
return new LinkedHashMap<>(0);
}
+ // must check for trailing & as the uri.split("&") will ignore those
+ if (uri.endsWith("&")) {
+ throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. "
+ + "Check the uri and remove the trailing & marker.");
+ }
+
// need to parse the uri query parameters manually as we cannot rely on splitting by &,
// as & can be used in a parameter value as well.
@@ -192,7 +194,15 @@ public final class URISupport {
}
// are we a raw value
- isRaw = value.toString().startsWith(RAW_TOKEN_START);
+ char rawTokenEnd = 0;
+ for (int j = 0; j < RAW_TOKEN_START.length; j++) {
+ String rawTokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[j];
+ isRaw = value.toString().startsWith(rawTokenStart);
+ if (isRaw) {
+ rawTokenEnd = RAW_TOKEN_END[j];
+ break;
+ }
+ }
// if we are in raw mode, then we keep adding until we hit the end marker
if (isRaw) {
@@ -202,9 +212,9 @@ public final class URISupport {
value.append(ch);
}
- // we only end the raw marker if its )& or at the end of the value
+ // we only end the raw marker if it's ")&", "}&", or at the end of the value
- boolean end = ch == RAW_TOKEN_END.charAt(0) && (next == '&' || next == '\u0000');
+ boolean end = ch == rawTokenEnd && (next == '&' || next == '\u0000');
if (end) {
// raw value end, so add that as a parameter, and reset flags
addParameter(key.toString(), value.toString(), rc, useRaw || isRaw);
@@ -302,6 +312,71 @@ public final class URISupport {
}
}
+ public static List<Pair<Integer>> scanRaw(String str) {
+ List<Pair<Integer>> answer = new ArrayList<>();
+ if (str == null || isEmpty(str)) {
+ return answer;
+ }
+
+ int offset = 0;
+ int start = str.indexOf(RAW_TOKEN_PREFIX);
+ while (start >= 0 && offset < str.length()) {
+ offset = start + RAW_TOKEN_PREFIX.length();
+ for (int i = 0; i < RAW_TOKEN_START.length; i++) {
+ String tokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[i];
+ char tokenEnd = RAW_TOKEN_END[i];
+ if (str.startsWith(tokenStart, start)) {
+ offset = scanRawToEnd(str, start, tokenStart, tokenEnd, answer);
+ continue;
+ }
+ }
+ start = str.indexOf(RAW_TOKEN_PREFIX, offset);
+ }
+ return answer;
+ }
+
+ private static int scanRawToEnd(String str, int start, String tokenStart, char tokenEnd,
+ List<Pair<Integer>> answer) {
+ // we search the first end bracket to close the RAW token
+ // as opposed to parsing query, this doesn't allow the occurrences of end brackets
+ // inbetween because this may be used on the host/path parts of URI
+ // and thus we cannot rely on '&' for detecting the end of a RAW token
+ int end = str.indexOf(tokenEnd, start + tokenStart.length());
+ if (end < 0) {
+ // still return a pair even if RAW token is not closed
+ answer.add(new Pair<>(start, str.length()));
+ return str.length();
+ }
+ answer.add(new Pair<>(start, end));
+ return end + 1;
+ }
+
+ public static boolean isRaw(int index, List<Pair<Integer>> pairs) {
+ for (Pair<Integer> pair : pairs) {
+ if (index < pair.getLeft()) {
+ return false;
+ }
+ if (index <= pair.getRight()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean resolveRaw(String str, BiConsumer<String, String> consumer) {
+ for (int i = 0; i < RAW_TOKEN_START.length; i++) {
+ String tokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[i];
+ String tokenEnd = String.valueOf(RAW_TOKEN_END[i]);
+ if (str.startsWith(tokenStart) && str.endsWith(tokenEnd)) {
+ String raw = str.substring(tokenStart.length(), str.length() - 1);
+ consumer.accept(str, raw);
+ return true;
+ }
+ }
+ // not RAW value
+ return false;
+ }
+
/**
* Assembles a query from the given map.
*
@@ -346,18 +421,20 @@ public final class URISupport {
} else {
rc.append(key);
}
+ if (value == null) {
+ return;
+ }
// only append if value is not null
- if (value != null) {
- rc.append("=");
- if (value.startsWith(RAW_TOKEN_START) && value.endsWith(RAW_TOKEN_END)) {
- // do not encode RAW parameters
- rc.append(value);
+ rc.append("=");
+ boolean isRaw = resolveRaw(value, (str, raw) -> {
+ // do not encode RAW parameters
+ rc.append(str);
+ });
+ if (!isRaw) {
+ if (encode) {
+ rc.append(URLEncoder.encode(value, CHARSET));
} else {
- if (encode) {
- rc.append(URLEncoder.encode(value, CHARSET));
- } else {
- rc.append(value);
- }
+ rc.append(value);
}
}
}
diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/UnsafeUriCharactersEncoder.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/UnsafeUriCharactersEncoder.java
index 9dbe30c..5391b44 100644
--- a/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/UnsafeUriCharactersEncoder.java
+++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/impl/UnsafeUriCharactersEncoder.java
@@ -19,8 +19,6 @@ package org.apache.camel.runtimecatalog.impl;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Encoder for unsafe URI characters.
@@ -32,7 +30,6 @@ public final class UnsafeUriCharactersEncoder {
private static BitSet unsafeCharactersHttp;
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'};
- private static final Pattern RAW_PATTERN = Pattern.compile("RAW\\([^\\)]+\\)");
static {
unsafeCharactersRfc1738 = new BitSet(256);
@@ -94,48 +91,11 @@ public final class UnsafeUriCharactersEncoder {
return encode(s, unsafeCharactersHttp, checkRaw);
}
- private static List<Pair> checkRAW(String s) {
- Matcher matcher = RAW_PATTERN.matcher(s);
- List<Pair> answer = new ArrayList<>();
- // Check all occurrences
- while (matcher.find()) {
- answer.add(new Pair(matcher.start(), matcher.end()));
- }
- return answer;
- }
-
- private static boolean isRaw(int index, List<Pair> pairs) {
- for (Pair pair : pairs) {
- if (index < pair.left) {
- return false;
- } else {
- if (index >= pair.left) {
- if (index <= pair.right) {
- return true;
- } else {
- continue;
- }
- }
- }
- }
- return false;
- }
-
- private static class Pair {
- int left;
- int right;
-
- Pair(int left, int right) {
- this.left = left;
- this.right = right;
- }
- }
-
// Just skip the encode for isRAW part
public static String encode(String s, BitSet unsafeCharacters, boolean checkRaw) {
- List<Pair> rawPairs;
+ List<Pair<Integer>> rawPairs;
if (checkRaw) {
- rawPairs = checkRAW(s);
+ rawPairs = URISupport.scanRaw(s);
} else {
rawPairs = new ArrayList<>();
}
@@ -170,7 +130,7 @@ public final class UnsafeUriCharactersEncoder {
char next = i + 1 < chars.length ? chars[i + 1] : ' ';
char next2 = i + 2 < chars.length ? chars[i + 2] : ' ';
- if (isHexDigit(next) && isHexDigit(next2) && !isRaw(i, rawPairs)) {
+ if (isHexDigit(next) && isHexDigit(next2) && !URISupport.isRaw(i, rawPairs)) {
// its already encoded (decimal encoded) so just append as is
sb.append(ch);
} else {
diff --git a/camel-core/src/main/java/org/apache/camel/support/DefaultComponent.java b/camel-core/src/main/java/org/apache/camel/support/DefaultComponent.java
index 4925f69..c2d6e50 100644
--- a/camel-core/src/main/java/org/apache/camel/support/DefaultComponent.java
+++ b/camel-core/src/main/java/org/apache/camel/support/DefaultComponent.java
@@ -45,7 +45,10 @@ import org.apache.camel.util.function.Suppliers;
*/
public abstract class DefaultComponent extends ServiceSupport implements Component {
- private static final Pattern RAW_PATTERN = Pattern.compile("RAW(.*&&.*)");
+ /**
+ * Simple RAW() pattern used only for validating URI in this class
+ */
+ private static final Pattern RAW_PATTERN = Pattern.compile("RAW[({].*&&.*[)}]");
private final List<Supplier<ComponentExtension>> extensions = new ArrayList<>();
diff --git a/camel-core/src/test/java/org/apache/camel/issues/EndpointWithRawUriParameterTest.java b/camel-core/src/test/java/org/apache/camel/issues/EndpointWithRawUriParameterTest.java
index 8fa2c4f..d2f9cc7 100644
--- a/camel-core/src/test/java/org/apache/camel/issues/EndpointWithRawUriParameterTest.java
+++ b/camel-core/src/test/java/org/apache/camel/issues/EndpointWithRawUriParameterTest.java
@@ -27,7 +27,6 @@ import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.support.DefaultComponent;
import org.apache.camel.support.DefaultEndpoint;
import org.apache.camel.support.DefaultProducer;
@@ -164,6 +163,17 @@ public class EndpointWithRawUriParameterTest extends ContextTestSupport {
assertMockEndpointsSatisfied();
}
+ @Test
+ public void testRawUriParameterOkDynamic() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(1);
+ getMockEndpoint("mock:result").expectedHeaderReceived("username", "scott");
+ getMockEndpoint("mock:result").expectedHeaderReceived("password", "foo)+bar");
+
+ template.sendBody("direct:okDynamic", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@@ -190,6 +200,10 @@ public class EndpointWithRawUriParameterTest extends ContextTestSupport {
from("direct:ok")
.to("mycomponent:foo?password=RAW(foo)+bar)&username=scott")
.to("mock:result");
+
+ from("direct:okDynamic")
+ .toD("mycomponent:foo?password=RAW{foo)+bar}&username=scott")
+ .to("mock:result");
}
};
}
diff --git a/camel-util/src/main/java/org/apache/camel/util/Pair.java b/camel-util/src/main/java/org/apache/camel/util/Pair.java
new file mode 100644
index 0000000..8f4e3b4
--- /dev/null
+++ b/camel-util/src/main/java/org/apache/camel/util/Pair.java
@@ -0,0 +1,60 @@
+/**
+ * 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.camel.util;
+
+import java.util.Objects;
+
+/**
+ * Generic holder object for pair values.
+ */
+public class Pair<T> {
+
+ private T left;
+ private T right;
+
+ public Pair(T left, T right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public T getLeft() {
+ return left;
+ }
+
+ public T getRight() {
+ return right;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Pair<?> that = (Pair<?>) o;
+ return Objects.equals(left, that.left) &&
+ Objects.equals(right, that.right);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(left, right);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + left + ", " + right + ")";
+ }
+}
diff --git a/camel-util/src/main/java/org/apache/camel/util/URIScanner.java b/camel-util/src/main/java/org/apache/camel/util/URIScanner.java
new file mode 100644
index 0000000..7bf813b
--- /dev/null
+++ b/camel-util/src/main/java/org/apache/camel/util/URIScanner.java
@@ -0,0 +1,270 @@
+/**
+ * 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.camel.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import static org.apache.camel.util.URISupport.RAW_TOKEN_END;
+import static org.apache.camel.util.URISupport.RAW_TOKEN_PREFIX;
+import static org.apache.camel.util.URISupport.RAW_TOKEN_START;
+
+/**
+ * RAW syntax aware URI scanner that provides various URI manipulations.
+ */
+class URIScanner {
+
+ private enum Mode {
+ KEY, VALUE
+ }
+
+ private static final char END = '\u0000';
+
+ private final String charset;
+ private final StringBuilder key;
+ private final StringBuilder value;
+ private Mode mode;
+ private boolean isRaw;
+ private char rawTokenEnd;
+
+ public URIScanner(String charset) {
+ this.charset = charset;
+ key = new StringBuilder();
+ value = new StringBuilder();
+ }
+
+ private void initState() {
+ mode = Mode.KEY;
+ key.setLength(0);
+ value.setLength(0);
+ isRaw = false;
+ }
+
+ private String getDecodedKey() throws UnsupportedEncodingException {
+ return URLDecoder.decode(key.toString(), charset);
+ }
+
+ private String getDecodedValue() throws UnsupportedEncodingException {
+ // need to replace % with %25
+ String s = StringHelper.replaceAll(value.toString(), "%", "%25");
+ String answer = URLDecoder.decode(s, charset);
+ return answer;
+ }
+
+ public Map<String, Object> parseQuery(String uri, boolean useRaw) throws URISyntaxException {
+ // need to parse the uri query parameters manually as we cannot rely on splitting by &,
+ // as & can be used in a parameter value as well.
+
+ try {
+ // use a linked map so the parameters is in the same order
+ Map<String, Object> answer = new LinkedHashMap<>();
+
+ initState();
+
+ // parse the uri parameters char by char
+ for (int i = 0; i < uri.length(); i++) {
+ // current char
+ char ch = uri.charAt(i);
+ // look ahead of the next char
+ char next;
+ if (i <= uri.length() - 2) {
+ next = uri.charAt(i + 1);
+ } else {
+ next = END;
+ }
+
+ switch (mode) {
+ case KEY:
+ // if there is a = sign then the key ends and we are in value mode
+ if (ch == '=') {
+ mode = Mode.VALUE;
+ continue;
+ }
+
+ if (ch != '&') {
+ // regular char so add it to the key
+ key.append(ch);
+ }
+ break;
+ case VALUE:
+ // are we a raw value
+ isRaw = checkRaw();
+
+ // if we are in raw mode, then we keep adding until we hit the end marker
+ if (isRaw) {
+ value.append(ch);
+
+ if (isAtEnd(ch, next)) {
+ // raw value end, so add that as a parameter, and reset flags
+ addParameter(answer, useRaw || isRaw);
+ initState();
+ // skip to next as we are in raw mode and have already added the value
+ i++;
+ }
+ continue;
+ }
+
+ if (ch != '&') {
+ // regular char so add it to the value
+ value.append(ch);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown mode: " + mode);
+ }
+
+ // the & denote parameter is ended
+ if (ch == '&') {
+ // parameter is ended, as we hit & separator
+ addParameter(answer, useRaw || isRaw);
+ initState();
+ }
+ }
+
+ // any left over parameters, then add that
+ if (key.length() > 0) {
+ addParameter(answer, useRaw || isRaw);
+ }
+
+ return answer;
+
+ } catch (UnsupportedEncodingException e) {
+ URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding");
+ se.initCause(e);
+ throw se;
+ }
+ }
+
+ private boolean checkRaw() {
+ rawTokenEnd = 0;
+
+ for (int i = 0; i < RAW_TOKEN_START.length; i++) {
+ String rawTokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[i];
+ boolean isRaw = value.toString().startsWith(rawTokenStart);
+ if (isRaw) {
+ rawTokenEnd = RAW_TOKEN_END[i];
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isAtEnd(char ch, char next) {
+ // we only end the raw marker if it's ")&", "}&", or at the end of the value
+ return ch == rawTokenEnd && (next == '&' || next == END);
+ }
+
+ private void addParameter(Map<String, Object> answer, boolean isRaw) throws UnsupportedEncodingException {
+ String name = getDecodedKey();
+ String value = isRaw ? this.value.toString() : getDecodedValue();
+
+ // does the key already exist?
+ if (answer.containsKey(name)) {
+ // yes it does, so make sure we can support multiple values, but using a list
+ // to hold the multiple values
+ Object existing = answer.get(name);
+ List<String> list;
+ if (existing instanceof List) {
+ list = CastUtils.cast((List<?>) existing);
+ } else {
+ // create a new list to hold the multiple values
+ list = new ArrayList<>();
+ String s = existing != null ? existing.toString() : null;
+ if (s != null) {
+ list.add(s);
+ }
+ }
+ list.add(value);
+ answer.put(name, list);
+ } else {
+ answer.put(name, value);
+ }
+ }
+
+ public static List<Pair<Integer>> scanRaw(String str) {
+ List<Pair<Integer>> answer = new ArrayList<>();
+ if (str == null || ObjectHelper.isEmpty(str)) {
+ return answer;
+ }
+
+ int offset = 0;
+ int start = str.indexOf(RAW_TOKEN_PREFIX);
+ while (start >= 0 && offset < str.length()) {
+ offset = start + RAW_TOKEN_PREFIX.length();
+ for (int i = 0; i < RAW_TOKEN_START.length; i++) {
+ String tokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[i];
+ char tokenEnd = RAW_TOKEN_END[i];
+ if (str.startsWith(tokenStart, start)) {
+ offset = scanRawToEnd(str, start, tokenStart, tokenEnd, answer);
+ continue;
+ }
+ }
+ start = str.indexOf(RAW_TOKEN_PREFIX, offset);
+ }
+ return answer;
+ }
+
+ private static int scanRawToEnd(String str, int start, String tokenStart, char tokenEnd,
+ List<Pair<Integer>> answer) {
+ // we search the first end bracket to close the RAW token
+ // as opposed to parsing query, this doesn't allow the occurrences of end brackets
+ // inbetween because this may be used on the host/path parts of URI
+ // and thus we cannot rely on '&' for detecting the end of a RAW token
+ int end = str.indexOf(tokenEnd, start + tokenStart.length());
+ if (end < 0) {
+ // still return a pair even if RAW token is not closed
+ answer.add(new Pair<>(start, str.length()));
+ return str.length();
+ }
+ answer.add(new Pair<>(start, end));
+ return end + 1;
+ }
+
+ public static boolean isRaw(int index, List<Pair<Integer>> pairs) {
+ for (Pair<Integer> pair : pairs) {
+ if (index < pair.getLeft()) {
+ return false;
+ }
+ if (index <= pair.getRight()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean resolveRaw(String str, BiConsumer<String, String> consumer) {
+ for (int i = 0; i < RAW_TOKEN_START.length; i++) {
+ String tokenStart = RAW_TOKEN_PREFIX + RAW_TOKEN_START[i];
+ String tokenEnd = String.valueOf(RAW_TOKEN_END[i]);
+ if (str.startsWith(tokenStart) && str.endsWith(tokenEnd)) {
+ String raw = str.substring(tokenStart.length(), str.length() - 1);
+ consumer.accept(str, raw);
+ return true;
+ }
+ }
+ // not RAW value
+ return false;
+ }
+
+}
diff --git a/camel-util/src/main/java/org/apache/camel/util/URISupport.java b/camel-util/src/main/java/org/apache/camel/util/URISupport.java
index 539e416..7c93201 100644
--- a/camel-util/src/main/java/org/apache/camel/util/URISupport.java
+++ b/camel-util/src/main/java/org/apache/camel/util/URISupport.java
@@ -19,7 +19,6 @@ package org.apache.camel.util;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
@@ -33,23 +32,25 @@ import java.util.regex.Pattern;
*/
public final class URISupport {
- public static final String RAW_TOKEN_START = "RAW(";
- public static final String RAW_TOKEN_END = ")";
+ public static final String RAW_TOKEN_PREFIX = "RAW";
+ public static final char[] RAW_TOKEN_START = { '(', '{' };
+ public static final char[] RAW_TOKEN_END = { ')', '}' };
// Match any key-value pair in the URI query string whose key contains
// "passphrase" or "password" or secret key (case-insensitive).
// First capture group is the key, second is the value.
- private static final Pattern SECRETS = Pattern.compile("([?&][^=]*(?:passphrase|password|secretKey)[^=]*)=(RAW\\(.*\\)|[^&]*)",
+ private static final Pattern SECRETS = Pattern.compile(
+ "([?&][^=]*(?:passphrase|password|secretKey)[^=]*)=(RAW[({].*[)}]|[^&]*)",
Pattern.CASE_INSENSITIVE);
-
+
// Match the user password in the URI as second capture group
// (applies to URI with authority component and userinfo token in the form "user:password").
private static final Pattern USERINFO_PASSWORD = Pattern.compile("(.*://.*:)(.*)(@)");
-
+
// Match the user password in the URI path as second capture group
// (applies to URI path with authority component and userinfo token in the form "user:password").
private static final Pattern PATH_USERINFO_PASSWORD = Pattern.compile("(.*:)(.*)(@)");
-
+
private static final String CHARSET = "UTF-8";
private URISupport() {
@@ -73,12 +74,12 @@ public final class URISupport {
}
return sanitized;
}
-
+
/**
* Removes detected sensitive information (such as passwords) from the
* <em>path part</em> of an URI (that is, the part without the query
* parameters or component prefix) and returns the result.
- *
+ *
* @param path the URI path to sanitize
* @return null if the path is null, otherwise the sanitized path
*/
@@ -122,6 +123,7 @@ public final class URISupport {
* @param uri the uri
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
+ * @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
@@ -140,6 +142,7 @@ public final class URISupport {
* @param useRaw whether to force using raw values
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
+ * @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
@@ -159,147 +162,61 @@ public final class URISupport {
* @param lenient whether to parse lenient and ignore trailing & markers which has no key or value which can happen when using HTTP components
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
+ * @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static Map<String, Object> parseQuery(String uri, boolean useRaw, boolean lenient) throws URISyntaxException {
- // must check for trailing & as the uri.split("&") will ignore those
- if (!lenient) {
- if (uri != null && uri.endsWith("&")) {
- throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. "
- + "Check the uri and remove the trailing & marker.");
- }
- }
-
if (uri == null || ObjectHelper.isEmpty(uri)) {
// return an empty map
return new LinkedHashMap<>(0);
}
- // need to parse the uri query parameters manually as we cannot rely on splitting by &,
- // as & can be used in a parameter value as well.
-
- try {
- // use a linked map so the parameters is in the same order
- Map<String, Object> rc = new LinkedHashMap<>();
-
- boolean isKey = true;
- boolean isValue = false;
- boolean isRaw = false;
- StringBuilder key = new StringBuilder();
- StringBuilder value = new StringBuilder();
-
- // parse the uri parameters char by char
- for (int i = 0; i < uri.length(); i++) {
- // current char
- char ch = uri.charAt(i);
- // look ahead of the next char
- char next;
- if (i <= uri.length() - 2) {
- next = uri.charAt(i + 1);
- } else {
- next = '\u0000';
- }
-
- // are we a raw value
- isRaw = value.toString().startsWith(RAW_TOKEN_START);
-
- // if we are in raw mode, then we keep adding until we hit the end marker
- if (isRaw) {
- if (isKey) {
- key.append(ch);
- } else if (isValue) {
- value.append(ch);
- }
-
- // we only end the raw marker if its )& or at the end of the value
-
- boolean end = ch == RAW_TOKEN_END.charAt(0) && (next == '&' || next == '\u0000');
- if (end) {
- // raw value end, so add that as a parameter, and reset flags
- addParameter(key.toString(), value.toString(), rc, useRaw || isRaw);
- key.setLength(0);
- value.setLength(0);
- isKey = true;
- isValue = false;
- isRaw = false;
- // skip to next as we are in raw mode and have already added the value
- i++;
- }
- continue;
- }
-
- // if its a key and there is a = sign then the key ends and we are in value mode
- if (isKey && ch == '=') {
- isKey = false;
- isValue = true;
- isRaw = false;
- continue;
- }
-
- // the & denote parameter is ended
- if (ch == '&') {
- // parameter is ended, as we hit & separator
- addParameter(key.toString(), value.toString(), rc, useRaw || isRaw);
- key.setLength(0);
- value.setLength(0);
- isKey = true;
- isValue = false;
- isRaw = false;
- continue;
- }
-
- // regular char so add it to the key or value
- if (isKey) {
- key.append(ch);
- } else if (isValue) {
- value.append(ch);
- }
- }
-
- // any left over parameters, then add that
- if (key.length() > 0) {
- addParameter(key.toString(), value.toString(), rc, useRaw || isRaw);
- }
+ // must check for trailing & as the uri.split("&") will ignore those
+ if (!lenient && uri.endsWith("&")) {
+ throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. "
+ + "Check the uri and remove the trailing & marker.");
+ }
- return rc;
+ URIScanner scanner = new URIScanner(CHARSET);
+ return scanner.parseQuery(uri, useRaw);
+ }
- } catch (UnsupportedEncodingException e) {
- URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding");
- se.initCause(e);
- throw se;
- }
+ /**
+ * Scans RAW tokens in the string and returns the list of pair indexes which tell where
+ * a RAW token starts and ends in the string.
+ * <p/>
+ * This is a companion method with {@link #isRaw(int, List)} and the returned value is
+ * supposed to be used as the parameter of that method.
+ *
+ * @param str the string to scan RAW tokens
+ * @return the list of pair indexes which represent the start and end positions of a RAW token
+ * @see #isRaw(int, List)
+ * @see #RAW_TOKEN_PREFIX
+ * @see #RAW_TOKEN_START
+ * @see #RAW_TOKEN_END
+ */
+ public static List<Pair<Integer>> scanRaw(String str) {
+ return URIScanner.scanRaw(str);
}
- private static void addParameter(String name, String value, Map<String, Object> map, boolean isRaw) throws UnsupportedEncodingException {
- name = URLDecoder.decode(name, CHARSET);
- if (!isRaw) {
- // need to replace % with %25
- String s = StringHelper.replaceAll(value, "%", "%25");
- value = URLDecoder.decode(s, CHARSET);
- }
-
- // does the key already exist?
- if (map.containsKey(name)) {
- // yes it does, so make sure we can support multiple values, but using a list
- // to hold the multiple values
- Object existing = map.get(name);
- List<String> list;
- if (existing instanceof List) {
- list = CastUtils.cast((List<?>) existing);
- } else {
- // create a new list to hold the multiple values
- list = new ArrayList<>();
- String s = existing != null ? existing.toString() : null;
- if (s != null) {
- list.add(s);
- }
- }
- list.add(value);
- map.put(name, list);
- } else {
- map.put(name, value);
- }
+ /**
+ * Tests if the index is within any pair of the start and end indexes which represent
+ * the start and end positions of a RAW token.
+ * <p/>
+ * This is a companion method with {@link #scanRaw(String)} and is supposed to consume
+ * the returned value of that method as the second parameter <tt>pairs</tt>.
+ *
+ * @param index the index to be tested
+ * @param pairs the list of pair indexes which represent the start and end positions of a RAW token
+ * @return <tt>true</tt> if the index is within any pair of the indexes, <tt>false</tt> otherwise
+ * @see #scanRaw(String)
+ * @see #RAW_TOKEN_PREFIX
+ * @see #RAW_TOKEN_START
+ * @see #RAW_TOKEN_END
+ */
+ public static boolean isRaw(int index, List<Pair<Integer>> pairs) {
+ return URIScanner.isRaw(index, pairs);
}
/**
@@ -333,35 +250,35 @@ public final class URISupport {
*
* @param parameters the uri parameters
* @see #parseQuery(String)
+ * @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
@SuppressWarnings("unchecked")
public static void resolveRawParameterValues(Map<String, Object> parameters) {
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
- if (entry.getValue() != null) {
- // if the value is a list then we need to iterate
- Object value = entry.getValue();
- if (value instanceof List) {
- List list = (List) value;
- for (int i = 0; i < list.size(); i++) {
- Object obj = list.get(i);
- if (obj != null) {
- String str = obj.toString();
- if (str.startsWith(RAW_TOKEN_START) && str.endsWith(RAW_TOKEN_END)) {
- str = str.substring(4, str.length() - 1);
- // update the string in the list
- list.set(i, str);
- }
- }
- }
- } else {
- String str = entry.getValue().toString();
- if (str.startsWith(RAW_TOKEN_START) && str.endsWith(RAW_TOKEN_END)) {
- str = str.substring(4, str.length() - 1);
- entry.setValue(str);
+ if (entry.getValue() == null) {
+ continue;
+ }
+ // if the value is a list then we need to iterate
+ Object value = entry.getValue();
+ if (value instanceof List) {
+ List list = (List) value;
+ for (int i = 0; i < list.size(); i++) {
+ Object obj = list.get(i);
+ if (obj == null) {
+ continue;
}
+ String str = obj.toString();
+ final int index = i;
+ URIScanner.resolveRaw(str, (s, raw) -> {
+ // update the string in the list
+ list.set(index, raw);
+ });
}
+ } else {
+ String str = entry.getValue().toString();
+ URIScanner.resolveRaw(str, (s, raw) -> entry.setValue(raw));
}
}
}
@@ -491,17 +408,19 @@ public final class URISupport {
private static void appendQueryStringParameter(String key, String value, StringBuilder rc) throws UnsupportedEncodingException {
rc.append(URLEncoder.encode(key, CHARSET));
+ if (value == null) {
+ return;
+ }
// only append if value is not null
- if (value != null) {
- rc.append("=");
- if (value.startsWith(RAW_TOKEN_START) && value.endsWith(RAW_TOKEN_END)) {
- // do not encode RAW parameters unless it has %
- // need to replace % with %25 to avoid losing "%" when decoding
- String s = StringHelper.replaceAll(value, "%", "%25");
- rc.append(s);
- } else {
- rc.append(URLEncoder.encode(value, CHARSET));
- }
+ rc.append("=");
+ boolean isRaw = URIScanner.resolveRaw(value, (str, raw) -> {
+ // do not encode RAW parameters unless it has %
+ // need to replace % with %25 to avoid losing "%" when decoding
+ String s = StringHelper.replaceAll(str, "%", "%25");
+ rc.append(s);
+ });
+ if (!isRaw) {
+ rc.append(URLEncoder.encode(value, CHARSET));
}
}
@@ -549,6 +468,7 @@ public final class URISupport {
* @return the normalized uri
* @throws URISyntaxException in thrown if the uri syntax is invalid
* @throws UnsupportedEncodingException is thrown if encoding error
+ * @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
diff --git a/camel-util/src/main/java/org/apache/camel/util/UnsafeUriCharactersEncoder.java b/camel-util/src/main/java/org/apache/camel/util/UnsafeUriCharactersEncoder.java
index 8273c53..d98b88d 100644
--- a/camel-util/src/main/java/org/apache/camel/util/UnsafeUriCharactersEncoder.java
+++ b/camel-util/src/main/java/org/apache/camel/util/UnsafeUriCharactersEncoder.java
@@ -19,8 +19,6 @@ package org.apache.camel.util;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Encoder for unsafe URI characters.
@@ -32,7 +30,6 @@ public final class UnsafeUriCharactersEncoder {
private static BitSet unsafeCharactersHttp;
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'};
- private static final Pattern RAW_PATTERN = Pattern.compile("RAW\\([^\\)]+\\)");
static {
unsafeCharactersRfc1738 = new BitSet(256);
@@ -94,48 +91,11 @@ public final class UnsafeUriCharactersEncoder {
return encode(s, unsafeCharactersHttp, checkRaw);
}
- private static List<Pair> checkRAW(String s) {
- Matcher matcher = RAW_PATTERN.matcher(s);
- List<Pair> answer = new ArrayList<>();
- // Check all occurrences
- while (matcher.find()) {
- // TODO: should likely be matcher.end() - 1
- answer.add(new Pair(matcher.start(), matcher.end()));
- }
- return answer;
- }
-
- private static boolean isRaw(int index, List<Pair>pairs) {
- for (Pair pair : pairs) {
- if (index < pair.left) {
- return false;
- } else {
- if (index >= pair.left) {
- if (index <= pair.right) {
- return true;
- } else {
- continue;
- }
- }
- }
- }
- return false;
- }
-
- private static class Pair {
- int left;
- int right;
- Pair(int left, int right) {
- this.left = left;
- this.right = right;
- }
- }
-
// Just skip the encode for isRAW part
public static String encode(String s, BitSet unsafeCharacters, boolean checkRaw) {
- List<Pair> rawPairs;
+ List<Pair<Integer>> rawPairs;
if (checkRaw) {
- rawPairs = checkRAW(s);
+ rawPairs = URISupport.scanRaw(s);
} else {
rawPairs = new ArrayList<>();
}
@@ -170,7 +130,7 @@ public final class UnsafeUriCharactersEncoder {
char next = i + 1 < chars.length ? chars[i + 1] : ' ';
char next2 = i + 2 < chars.length ? chars[i + 2] : ' ';
- if (isHexDigit(next) && isHexDigit(next2) && !isRaw(i, rawPairs)) {
+ if (isHexDigit(next) && isHexDigit(next2) && !URISupport.isRaw(i, rawPairs)) {
// its already encoded (decimal encoded) so just append as is
sb.append(ch);
} else {
diff --git a/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java b/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java
index 7a79f2e..63e5dc5 100644
--- a/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java
+++ b/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java
@@ -18,14 +18,17 @@ package org.apache.camel.util;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -258,16 +261,20 @@ public class URISupportTest {
@Test
public void testSanitizeUriWithRawPassword() {
- String uri = "http://foo?username=me&password=RAW(me#@123)&foo=bar";
+ String uri1 = "http://foo?username=me&password=RAW(me#@123)&foo=bar";
+ String uri2 = "http://foo?username=me&password=RAW{me#@123}&foo=bar";
String expected = "http://foo?username=me&password=xxxxxx&foo=bar";
- assertEquals(expected, URISupport.sanitizeUri(uri));
+ assertEquals(expected, URISupport.sanitizeUri(uri1));
+ assertEquals(expected, URISupport.sanitizeUri(uri2));
}
@Test
public void testSanitizeUriRawUnsafePassword() {
- String uri = "sftp://localhost/target?password=RAW(beforeAmp&afterAmp)&username=jrandom";
+ String uri1 = "sftp://localhost/target?password=RAW(beforeAmp&afterAmp)&username=jrandom";
+ String uri2 = "sftp://localhost/target?password=RAW{beforeAmp&afterAmp}&username=jrandom";
String expected = "sftp://localhost/target?password=xxxxxx&username=jrandom";
- assertEquals(expected, URISupport.sanitizeUri(uri));
+ assertEquals(expected, URISupport.sanitizeUri(uri1));
+ assertEquals(expected, URISupport.sanitizeUri(uri2));
}
@Test
@@ -302,6 +309,16 @@ public class URISupportTest {
}
@Test
+ public void testRawParameterCurly() throws Exception {
+ String out = URISupport.normalizeUri("xmpp://camel-user@localhost:123/test-user@localhost?password=RAW{++?w0rd}&serviceName=some chat");
+ assertEquals("xmpp://camel-user@localhost:123/test-user@localhost?password=RAW{++?w0rd}&serviceName=some+chat", out);
+
+ String out2 = URISupport.normalizeUri("xmpp://camel-user@localhost:123/test-user@localhost?password=RAW{foo %% bar}&serviceName=some chat");
+ // Just make sure the RAW parameter can be resolved rightly, we need to replace the % into %25
+ assertEquals("xmpp://camel-user@localhost:123/test-user@localhost?password=RAW{foo %25%25 bar}&serviceName=some+chat", out2);
+ }
+
+ @Test
public void testParseQuery() throws Exception {
Map<String, Object> map = URISupport.parseQuery("password=secret&serviceName=somechat");
assertEquals(2, map.size());
@@ -325,6 +342,24 @@ public class URISupportTest {
}
@Test
+ public void testParseQueryCurly() throws Exception {
+ Map<String, Object> map = URISupport.parseQuery("password=RAW{++?w0rd}&serviceName=somechat");
+ assertEquals(2, map.size());
+ assertEquals("RAW{++?w0rd}", map.get("password"));
+ assertEquals("somechat", map.get("serviceName"));
+
+ map = URISupport.parseQuery("password=RAW{++?)w&rd}&serviceName=somechat");
+ assertEquals(2, map.size());
+ assertEquals("RAW{++?)w&rd}", map.get("password"));
+ assertEquals("somechat", map.get("serviceName"));
+
+ map = URISupport.parseQuery("password=RAW{%2520w&rd}&serviceName=somechat");
+ assertEquals(2, map.size());
+ assertEquals("RAW{%2520w&rd}", map.get("password"));
+ assertEquals("somechat", map.get("serviceName"));
+ }
+
+ @Test
public void testParseQueryLenient() throws Exception {
try {
URISupport.parseQuery("password=secret&serviceName=somechat&", false, false);
@@ -340,6 +375,48 @@ public class URISupportTest {
}
@Test
+ public void testScanRaw() {
+ List<Pair<Integer>> pairs1 = URISupport.scanRaw("password=RAW(++?5w0rd)&serviceName=somechat");
+ assertEquals(1, pairs1.size());
+ assertEquals(new Pair(9, 21), pairs1.get(0));
+
+ List<Pair<Integer>> pairs2 = URISupport.scanRaw("password=RAW{++?5w0rd}&serviceName=somechat");
+ assertEquals(1, pairs2.size());
+ assertEquals(new Pair(9, 21), pairs2.get(0));
+
+ List<Pair<Integer>> pairs3 = URISupport.scanRaw("password=RAW{++?)&0rd}&serviceName=somechat");
+ assertEquals(1, pairs3.size());
+ assertEquals(new Pair(9, 21), pairs3.get(0));
+
+ List<Pair<Integer>> pairs4 = URISupport.scanRaw("password1=RAW(++?}&0rd)&password2=RAW{++?)&0rd}&serviceName=somechat");
+ assertEquals(2, pairs4.size());
+ assertEquals(new Pair(10, 22), pairs4.get(0));
+ assertEquals(new Pair(34, 46), pairs4.get(1));
+ }
+
+ @Test
+ public void testIsRaw() {
+ List<Pair<Integer>> pairs = Arrays.asList(
+ new Pair(3, 5),
+ new Pair(8, 10));
+ for (int i = 0; i < 3; i++) {
+ assertFalse(URISupport.isRaw(i, pairs));
+ }
+ for (int i = 3; i < 6; i++) {
+ assertTrue(URISupport.isRaw(i, pairs));
+ }
+ for (int i = 6; i < 8; i++) {
+ assertFalse(URISupport.isRaw(i, pairs));
+ }
+ for (int i = 8; i < 11; i++) {
+ assertTrue(URISupport.isRaw(i, pairs));
+ }
+ for (int i = 11; i < 15; i++) {
+ assertFalse(URISupport.isRaw(i, pairs));
+ }
+ }
+
+ @Test
public void testResolveRawParameterValues() throws Exception {
Map<String, Object> map = URISupport.parseQuery("password=secret&serviceName=somechat");
URISupport.resolveRawParameterValues(map);
@@ -361,6 +438,21 @@ public class URISupportTest {
}
@Test
+ public void testResolveRawParameterValuesCurly() throws Exception {
+ Map<String, Object> map = URISupport.parseQuery("password=RAW{++?w0rd}&serviceName=somechat");
+ URISupport.resolveRawParameterValues(map);
+ assertEquals(2, map.size());
+ assertEquals("++?w0rd", map.get("password"));
+ assertEquals("somechat", map.get("serviceName"));
+
+ map = URISupport.parseQuery("password=RAW{++?)w&rd}&serviceName=somechat");
+ URISupport.resolveRawParameterValues(map);
+ assertEquals(2, map.size());
+ assertEquals("++?)w&rd", map.get("password"));
+ assertEquals("somechat", map.get("serviceName"));
+ }
+
+ @Test
public void testAppendParameterToUriAndReplaceExistingOne() throws Exception {
Map<String, Object> newParameters = new HashMap<>();
newParameters.put("foo", "456");