diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index e82cd63978..e605643b6a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -225,6 +225,8 @@ class URL implements Serializable {
}
/**
+ * NOTICE: This method allocate too much objects, we can use {@link URLStrParser#parseDecodedStr(String)} instead.
+ *
* Parse url string
*
* @param url URL string
@@ -326,13 +328,16 @@ class URL implements Serializable {
String methodsString = parameters.get(METHODS_KEY);
if (StringUtils.isNotEmpty(methodsString)) {
- String[] methods = methodsString.split(",");
+ List methods = StringUtils.splitToList(methodsString, ',');
for (Map.Entry entry : parameters.entrySet()) {
String key = entry.getKey();
- for (String method : methods) {
- String methodPrefix = method + '.';
- if (key.startsWith(methodPrefix)) {
- String realKey = key.substring(methodPrefix.length());
+ for (int i = 0; i < methods.size(); i++) {
+ String method = methods.get(i);
+ int methodLen = method.length();
+ if (key.length() > methodLen
+ && key.startsWith(method)
+ && key.charAt(methodLen) == '.') {//equals to: key.startsWith(method + '.')
+ String realKey = key.substring(methodLen + 1);
URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
}
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
new file mode 100644
index 0000000000..37afb78143
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
@@ -0,0 +1,348 @@
+/*
+ * 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.dubbo.common;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING;
+import static org.apache.dubbo.common.utils.StringUtils.decodeHexByte;
+import static org.apache.dubbo.common.utils.Utf8Utils.decodeUtf8;
+
+public final class URLStrParser {
+
+ private static final char SPACE = 0x20;
+
+ private static final ThreadLocal DECODE_TEMP_BUF = ThreadLocal.withInitial(() -> new TempBuf(1024));
+
+ private URLStrParser() {
+ //empty
+ }
+
+ /**
+ * @param decodedURLStr : after {@link URL#decode} string
+ * decodedURLStr format: protocol://username:password@host:port/path?k1=v1&k2=v2
+ * [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2]
+ */
+ public static URL parseDecodedStr(String decodedURLStr) {
+ Map parameters = null;
+ int pathEndIdx = decodedURLStr.indexOf('?');
+ if (pathEndIdx >= 0) {
+ parameters = parseDecodedParams(decodedURLStr, pathEndIdx + 1);
+ } else {
+ pathEndIdx = decodedURLStr.length();
+ }
+
+ String decodedBody = decodedURLStr.substring(0, pathEndIdx);
+ return parseURLBody(decodedURLStr, decodedBody, parameters);
+ }
+
+ private static Map parseDecodedParams(String str, int from) {
+ int len = str.length();
+ if (from >= len) {
+ return Collections.emptyMap();
+ }
+
+ TempBuf tempBuf = DECODE_TEMP_BUF.get();
+ Map params = new HashMap<>();
+ int nameStart = from;
+ int valueStart = -1;
+ int i;
+ for (i = from; i < len; i++) {
+ char ch = str.charAt(i);
+ switch (ch) {
+ case '=':
+ if (nameStart == i) {
+ nameStart = i + 1;
+ } else if (valueStart < nameStart) {
+ valueStart = i + 1;
+ }
+ break;
+ case ';':
+ case '&':
+ addParam(str, false, nameStart, valueStart, i, params, tempBuf);
+ nameStart = i + 1;
+ break;
+ default:
+ // continue
+ }
+ }
+ addParam(str, false, nameStart, valueStart, i, params, tempBuf);
+ return params;
+ }
+
+ /**
+ * @param fullURLStr : fullURLString
+ * @param decodedBody : format: [protocol://][username:password@][host:port]/[path]
+ * @param parameters :
+ * @return URL
+ */
+ private static URL parseURLBody(String fullURLStr, String decodedBody, Map parameters) {
+ int starIdx = 0, endIdx = decodedBody.length();
+ String protocol = null;
+ int protoEndIdx = decodedBody.indexOf("://");
+ if (protoEndIdx >= 0) {
+ if (protoEndIdx == 0) {
+ throw new IllegalStateException("url missing protocol: \"" + fullURLStr + "\"");
+ }
+ protocol = decodedBody.substring(0, protoEndIdx);
+ starIdx = protoEndIdx + 3;
+ } else {
+ // case: file:/path/to/file.txt
+ protoEndIdx = decodedBody.indexOf(":/");
+ if (protoEndIdx >= 0) {
+ if (protoEndIdx == 0) {
+ throw new IllegalStateException("url missing protocol: \"" + fullURLStr + "\"");
+ }
+ protocol = decodedBody.substring(0, protoEndIdx);
+ starIdx = protoEndIdx + 1;
+ }
+ }
+
+ String path = null;
+ int pathStartIdx = indexOf(decodedBody, '/', starIdx, endIdx);
+ if (pathStartIdx >= 0) {
+ path = decodedBody.substring(pathStartIdx + 1);
+ endIdx = pathStartIdx;
+ }
+
+ String username = null;
+ String password = null;
+ int pwdEndIdx = lastIndexOf(decodedBody, '@', starIdx, endIdx);
+ if (pwdEndIdx > 0) {
+ int userNameEndIdx = indexOf(decodedBody, ':', starIdx, pwdEndIdx);
+ username = decodedBody.substring(starIdx, userNameEndIdx);
+ password = decodedBody.substring(userNameEndIdx + 1, pwdEndIdx);
+ starIdx = pwdEndIdx + 1;
+ }
+
+ String host = null;
+ int port = 0;
+ int hostEndIdx = lastIndexOf(decodedBody, ':', starIdx, endIdx);
+ if (hostEndIdx > 0 && hostEndIdx < decodedBody.length() - 1) {
+ if (lastIndexOf(decodedBody, '%', starIdx, endIdx) > hostEndIdx) {
+ // ipv6 address with scope id
+ // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
+ // see https://howdoesinternetwork.com/2013/ipv6-zone-id
+ // ignore
+ } else {
+ port = Integer.parseInt(decodedBody.substring(hostEndIdx + 1, endIdx));
+ endIdx = hostEndIdx;
+ }
+ }
+
+ if (endIdx > starIdx) {
+ host = decodedBody.substring(starIdx, endIdx);
+ }
+ return new URL(protocol, username, password, host, port, path, parameters);
+ }
+
+ /**
+ * @param encodedURLStr : after {@link URL#encode(String)} string
+ * encodedURLStr after decode format: protocol://username:password@host:port/path?k1=v1&k2=v2
+ * [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2]
+ */
+ public static URL parseEncodedStr(String encodedURLStr) {
+ Map parameters = null;
+ int pathEndIdx = encodedURLStr.indexOf("%3F");// '?'
+ if (pathEndIdx >= 0) {
+ parameters = parseEncodedParams(encodedURLStr, pathEndIdx + 3);
+ } else {
+ pathEndIdx = encodedURLStr.length();
+ }
+
+ //decodedBody format: [protocol://][username:password@][host:port]/[path]
+ String decodedBody = decodeComponent(encodedURLStr, 0, pathEndIdx, false, DECODE_TEMP_BUF.get());
+ return parseURLBody(encodedURLStr, decodedBody, parameters);
+ }
+
+ private static Map parseEncodedParams(String str, int from) {
+ int len = str.length();
+ if (from >= len) {
+ return Collections.emptyMap();
+ }
+
+ TempBuf tempBuf = DECODE_TEMP_BUF.get();
+ Map params = new HashMap<>();
+ int nameStart = from;
+ int valueStart = -1;
+ int i;
+ for (i = from; i < len; i++) {
+ char ch = str.charAt(i);
+ if (ch == '%') {
+ if (i + 3 > len) {
+ throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + str);
+ }
+ ch = (char) decodeHexByte(str, i + 1);
+ i += 2;
+ }
+
+ switch (ch) {
+ case '=':
+ if (nameStart == i) {
+ nameStart = i + 1;
+ } else if (valueStart < nameStart) {
+ valueStart = i + 1;
+ }
+ break;
+ case ';':
+ case '&':
+ addParam(str, true, nameStart, valueStart, i - 2, params, tempBuf);
+ nameStart = i + 1;
+ break;
+ default:
+ // continue
+ }
+ }
+ addParam(str, true, nameStart, valueStart, i, params, tempBuf);
+ return params;
+ }
+
+ private static boolean addParam(String str, boolean isEncoded, int nameStart, int valueStart, int valueEnd, Map params,
+ TempBuf tempBuf) {
+ if (nameStart >= valueEnd) {
+ return false;
+ }
+
+ if (valueStart <= nameStart) {
+ valueStart = valueEnd + 1;
+ }
+
+ if (isEncoded) {
+ String name = decodeComponent(str, nameStart, valueStart - 3, false, tempBuf);
+ String value = decodeComponent(str, valueStart, valueEnd, false, tempBuf);
+ params.put(name, value);
+ } else {
+ String name = str.substring(nameStart, valueStart -1);
+ String value = str.substring(valueStart, valueEnd);
+ params.put(name, value);
+ }
+ return true;
+ }
+
+ private static String decodeComponent(String s, int from, int toExcluded, boolean isPath, TempBuf tempBuf) {
+ int len = toExcluded - from;
+ if (len <= 0) {
+ return EMPTY_STRING;
+ }
+
+ int firstEscaped = -1;
+ for (int i = from; i < toExcluded; i++) {
+ char c = s.charAt(i);
+ if (c == '%' || c == '+' && !isPath) {
+ firstEscaped = i;
+ break;
+ }
+ }
+ if (firstEscaped == -1) {
+ return s.substring(from, toExcluded);
+ }
+
+ // Each encoded byte takes 3 characters (e.g. "%20")
+ int decodedCapacity = (toExcluded - firstEscaped) / 3;
+ byte[] buf = tempBuf.byteBuf(decodedCapacity);
+ char[] charBuf = tempBuf.charBuf(len);
+ s.getChars(from, firstEscaped, charBuf, 0);
+
+ int charBufIdx = firstEscaped - from;
+ return decodeUtf8Component(s, firstEscaped, toExcluded, isPath, buf, charBuf, charBufIdx);
+ }
+
+ private static String decodeUtf8Component(String str, int firstEscaped, int toExcluded, boolean isPath, byte[] buf,
+ char[] charBuf, int charBufIdx) {
+ int bufIdx;
+ for (int i = firstEscaped; i < toExcluded; i++) {
+ char c = str.charAt(i);
+ if (c != '%') {
+ charBuf[charBufIdx++] = c != '+' || isPath ? c : SPACE;
+ continue;
+ }
+
+ bufIdx = 0;
+ do {
+ if (i + 3 > toExcluded) {
+ throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + str);
+ }
+ buf[bufIdx++] = decodeHexByte(str, i + 1);
+ i += 3;
+ } while (i < toExcluded && str.charAt(i) == '%');
+ i--;
+
+ charBufIdx += decodeUtf8(buf, 0, bufIdx, charBuf, charBufIdx);
+ }
+ return new String(charBuf, 0, charBufIdx);
+ }
+
+ private static int indexOf(String str, char ch, int from, int toExclude) {
+ from = Math.max(from, 0);
+ toExclude = Math.min(toExclude, str.length());
+ if (from > toExclude) {
+ return -1;
+ }
+
+ for (int i = from; i < toExclude; i++) {
+ if (str.charAt(i) == ch) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static int lastIndexOf(String str, char ch, int from, int toExclude) {
+ from = Math.max(from, 0);
+ toExclude = Math.min(toExclude, str.length() - 1);
+ if (from > toExclude) {
+ return -1;
+ }
+
+ for (int i = toExclude; i >= from; i--) {
+ if (str.charAt(i) == ch) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static final class TempBuf {
+
+ private final char[] chars;
+
+ private final byte[] bytes;
+
+ TempBuf(int bufSize) {
+ this.chars = new char[bufSize];
+ this.bytes = new byte[bufSize];
+ }
+
+ public char[] charBuf(int size) {
+ char[] chars = this.chars;
+ if (size <= chars.length) {
+ return chars;
+ }
+ return new char[size];
+ }
+
+ public byte[] byteBuf(int size) {
+ byte[] bytes = this.bytes;
+ if (size <= bytes.length) {
+ return bytes;
+ }
+ return new byte[size];
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index a941484dfb..fe410e2feb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -17,6 +17,8 @@
package org.apache.dubbo.common.constants;
+import org.apache.dubbo.common.URL;
+
import java.net.NetworkInterface;
import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;
@@ -52,6 +54,8 @@ public interface CommonConstants {
String PROTOCOL_SEPARATOR = "://";
+ String PROTOCOL_SEPARATOR_ENCODED = URL.encode(PROTOCOL_SEPARATOR);
+
String REGISTRY_SEPARATOR = "|";
Pattern REGISTRY_SPLIT_PATTERN = Pattern.compile("\\s*[|;]+\\s*");
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
index 9b910325bc..42ee05bbe5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
@@ -24,6 +24,7 @@ import com.alibaba.fastjson.JSON;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -60,6 +61,8 @@ public final class StringUtils {
private static final Pattern PARAMETERS_PATTERN = Pattern.compile("^\\[((\\s*\\{\\s*[\\w_\\-\\.]+\\s*:\\s*.+?\\s*\\}\\s*,?\\s*)+)\\s*\\]$");
private static final Pattern PAIR_PARAMETERS_PATTERN = Pattern.compile("^\\{\\s*([\\w-_\\.]+)\\s*:\\s*(.+)\\s*\\}$");
private static final int PAD_LIMIT = 8192;
+ private static final byte[] HEX2B;
+
/**
* @since 2.7.5
@@ -88,6 +91,33 @@ public final class StringUtils {
public static final String HYPHEN = valueOf(HYPHEN_CHAR);
+ static {
+ HEX2B = new byte[128];
+ Arrays.fill(HEX2B, (byte) -1);
+ HEX2B['0'] = (byte) 0;
+ HEX2B['1'] = (byte) 1;
+ HEX2B['2'] = (byte) 2;
+ HEX2B['3'] = (byte) 3;
+ HEX2B['4'] = (byte) 4;
+ HEX2B['5'] = (byte) 5;
+ HEX2B['6'] = (byte) 6;
+ HEX2B['7'] = (byte) 7;
+ HEX2B['8'] = (byte) 8;
+ HEX2B['9'] = (byte) 9;
+ HEX2B['A'] = (byte) 10;
+ HEX2B['B'] = (byte) 11;
+ HEX2B['C'] = (byte) 12;
+ HEX2B['D'] = (byte) 13;
+ HEX2B['E'] = (byte) 14;
+ HEX2B['F'] = (byte) 15;
+ HEX2B['a'] = (byte) 10;
+ HEX2B['b'] = (byte) 11;
+ HEX2B['c'] = (byte) 12;
+ HEX2B['d'] = (byte) 13;
+ HEX2B['e'] = (byte) 14;
+ HEX2B['f'] = (byte) 15;
+ }
+
private StringUtils() {
}
@@ -1000,4 +1030,25 @@ public final class StringUtils {
}
return parameters;
}
+
+ public static int decodeHexNibble(final char c) {
+ // Character.digit() is not used here, as it addresses a larger
+ // set of characters (both ASCII and full-width latin letters).
+ byte[] hex2b = HEX2B;
+ return c < hex2b.length ? hex2b[c] : -1;
+ }
+
+ /**
+ * Decode a 2-digit hex byte from within a string.
+ */
+ public static byte decodeHexByte(CharSequence s, int pos) {
+ int hi = decodeHexNibble(s.charAt(pos));
+ int lo = decodeHexNibble(s.charAt(pos + 1));
+ if (hi == -1 || lo == -1) {
+ throw new IllegalArgumentException(String.format(
+ "invalid hex byte '%s' at index %d of '%s'", s.subSequence(pos, pos + 2), pos, s));
+ }
+ return (byte) ((hi << 4) + lo);
+ }
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java
new file mode 100644
index 0000000000..09d5508e22
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java
@@ -0,0 +1,229 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package org.apache.dubbo.common.utils;
+
+import static java.lang.Character.MIN_HIGH_SURROGATE;
+import static java.lang.Character.MIN_LOW_SURROGATE;
+import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
+
+/**
+ * See original Utf8.java
+ */
+public final class Utf8Utils {
+
+ private Utf8Utils() {
+ //empty
+ }
+
+ public static int decodeUtf8(byte[] srcBytes, int srcIdx, int srcSize, char[] destChars, int destIdx) {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((srcIdx | srcSize | srcBytes.length - srcIdx - srcSize) < 0
+ || (destIdx | destChars.length - destIdx - srcSize) < 0) {
+ String exMsg = String.format("buffer srcBytes.length=%d, srcIdx=%d, srcSize=%d, destChars.length=%d, " +
+ "destIdx=%d", srcBytes.length, srcIdx, srcSize, destChars.length, destIdx);
+ throw new ArrayIndexOutOfBoundsException(
+ exMsg);
+ }
+
+ int offset = srcIdx;
+ final int limit = offset + srcSize;
+ final int destIdx0 = destIdx;
+
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (offset < limit) {
+ byte b = srcBytes[offset];
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByteSafe(b, destChars, destIdx++);
+ }
+
+ while (offset < limit) {
+ byte byte1 = srcBytes[offset++];
+ if (DecodeUtil.isOneByte(byte1)) {
+ DecodeUtil.handleOneByteSafe(byte1, destChars, destIdx++);
+ // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+ // extra optimized loop to take care of these runs.
+ while (offset < limit) {
+ byte b = srcBytes[offset];
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByteSafe(b, destChars, destIdx++);
+ }
+ } else if (DecodeUtil.isTwoBytes(byte1)) {
+ if (offset >= limit) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ DecodeUtil.handleTwoBytesSafe(byte1, /* byte2 */ srcBytes[offset++], destChars, destIdx++);
+ } else if (DecodeUtil.isThreeBytes(byte1)) {
+ if (offset >= limit - 1) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ DecodeUtil.handleThreeBytesSafe(
+ byte1,
+ /* byte2 */ srcBytes[offset++],
+ /* byte3 */ srcBytes[offset++],
+ destChars,
+ destIdx++);
+ } else {
+ if (offset >= limit - 2) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ DecodeUtil.handleFourBytesSafe(
+ byte1,
+ /* byte2 */ srcBytes[offset++],
+ /* byte3 */ srcBytes[offset++],
+ /* byte4 */ srcBytes[offset++],
+ destChars,
+ destIdx);
+ destIdx += 2;
+ }
+ }
+ return destIdx - destIdx0;
+ }
+
+
+ private static class DecodeUtil {
+
+ /**
+ * Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'.
+ */
+ private static boolean isOneByte(byte b) {
+ return b >= 0;
+ }
+
+ /**
+ * Returns whether this is a two-byte codepoint with the form '10XXXXXX'.
+ */
+ private static boolean isTwoBytes(byte b) {
+ return b < (byte) 0xE0;
+ }
+
+ /**
+ * Returns whether this is a three-byte codepoint with the form '110XXXXX'.
+ */
+ private static boolean isThreeBytes(byte b) {
+ return b < (byte) 0xF0;
+ }
+
+ private static void handleOneByteSafe(byte byte1, char[] resultArr, int resultPos) {
+ resultArr[resultPos] = (char) byte1;
+ }
+
+ private static void handleTwoBytesSafe(byte byte1, byte byte2, char[] resultArr, int resultPos) {
+ checkUtf8(byte1, byte2);
+ resultArr[resultPos] = (char) (((byte1 & 0x1F) << 6) | trailingByteValue(byte2));
+ }
+
+ private static void checkUtf8(byte byte1, byte byte2) {
+ // Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and
+ // overlong 2-byte, '11000001'.
+ if (byte1 < (byte) 0xC2 || isNotTrailingByte(byte2)) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ }
+
+ private static void handleThreeBytesSafe(byte byte1, byte byte2, byte byte3, char[] resultArr, int resultPos) {
+ checkUtf8(byte1, byte2, byte3);
+ resultArr[resultPos] =
+ (char) (((byte1 & 0x0F) << 12) | (trailingByteValue(byte2) << 6) | trailingByteValue(byte3));
+ }
+
+ private static void checkUtf8(byte byte1, byte byte2, byte byte3) {
+ if (isNotTrailingByte(byte2)
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ || isNotTrailingByte(byte3)) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ }
+
+ private static void handleFourBytesSafe(byte byte1, byte byte2, byte byte3, byte byte4, char[] resultArr,
+ int resultPos) {
+ checkUtf8(byte1, byte2, byte3, byte4);
+ int codepoint =
+ ((byte1 & 0x07) << 18)
+ | (trailingByteValue(byte2) << 12)
+ | (trailingByteValue(byte3) << 6)
+ | trailingByteValue(byte4);
+
+ resultArr[resultPos] = DecodeUtil.highSurrogate(codepoint);
+ resultArr[resultPos + 1] = DecodeUtil.lowSurrogate(codepoint);
+ }
+
+ private static void checkUtf8(byte byte1, byte byte2, byte byte3, byte byte4) {
+ if (isNotTrailingByte(byte2)
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // valid 4-byte leading byte?
+ // if (byte1 > (byte) 0xF4 ||
+ // overlong? 4 most significant bits must not all be zero
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // codepoint larger than the highest code point (U+10FFFF)?
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ || isNotTrailingByte(byte3)
+ || isNotTrailingByte(byte4)) {
+ throw new IllegalArgumentException("invalid UTF-8.");
+ }
+ }
+
+ /**
+ * Returns whether the byte is not a valid continuation of the form '10XXXXXX'.
+ */
+ private static boolean isNotTrailingByte(byte b) {
+ return b > (byte) 0xBF;
+ }
+
+ /**
+ * Returns the actual value of the trailing byte (removes the prefix '10') for composition.
+ */
+ private static int trailingByteValue(byte b) {
+ return b & 0x3F;
+ }
+
+ private static char highSurrogate(int codePoint) {
+ return (char)
+ ((MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10)) + (codePoint >>> 10));
+ }
+
+ private static char lowSurrogate(int codePoint) {
+ return (char) (MIN_LOW_SURROGATE + (codePoint & 0x3ff));
+ }
+ }
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
new file mode 100644
index 0000000000..3ca62ce83c
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.dubbo.common;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Created by LinShunkang on 2020/03/12
+ */
+public class URLStrParserTest {
+
+ @Test
+ public void test() {
+ String str = "dubbo%3A%2F%2Fadmin%3Aadmin123%40192.168.1.41%3A28113%2Forg.test.api.DemoService%24Iface%3Fanyhost%3Dtrue%26application%3Ddemo-service%26dubbo%3D2.6.1%26generic%3Dfalse%26interface%3Dorg.test.api.DemoService%24Iface%26methods%3DorbCompare%2CcheckText%2CcheckPicture%26pid%3D65557%26revision%3D1.4.17%26service.filter%3DbootMetrics%26side%3Dprovider%26status%3Dserver%26threads%3D200%26timestamp%3D1583136298859%26version%3D1.0.0";
+ System.out.println(URLStrParser.parseEncodedStr(str));
+
+ String decodeStr = URL.decode(str);
+ URL originalUrl = URL.valueOf(decodeStr);
+ assertThat(URLStrParser.parseEncodedStr(str), equalTo(originalUrl));
+ assertThat(URLStrParser.parseDecodedStr(decodeStr), equalTo(originalUrl));
+ }
+
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
index 1ef4d1dc49..db5f57b1c5 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
@@ -40,6 +40,7 @@ public class URLTest {
@Test
public void test_valueOf_noProtocolAndHost() throws Exception {
URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -51,9 +52,9 @@ public class URLTest {
assertEquals("1.0.0", url.getParameter("version"));
assertEquals("morgan", url.getParameter("application"));
-
url = URL.valueOf("context/path?version=1.0.0&application=morgan");
// ^^^^^^^ Caution , parse as host
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -65,9 +66,19 @@ public class URLTest {
assertEquals("morgan", url.getParameter("application"));
}
+ private void assertURLStrDecoder(URL url) {
+ String fullURLStr = url.toFullString();
+ URL newUrl = URLStrParser.parseEncodedStr(URL.encode(fullURLStr));
+ assertEquals(URL.valueOf(fullURLStr), newUrl);
+
+ URL newUrl2 = URLStrParser.parseDecodedStr(fullURLStr);
+ assertEquals(URL.valueOf(fullURLStr), newUrl2);
+ }
+
@Test
public void test_valueOf_noProtocol() throws Exception {
URL url = URL.valueOf("10.20.130.230");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -78,6 +89,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("10.20.130.230:20880");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -88,6 +100,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("10.20.130.230/context/path");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -98,6 +111,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("10.20.130.230:20880/context/path");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -108,6 +122,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -123,6 +138,7 @@ public class URLTest {
@Test
public void test_valueOf_noHost() throws Exception {
URL url = URL.valueOf("file:///home/user1/router.js");
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -135,6 +151,7 @@ public class URLTest {
// Caution!!
url = URL.valueOf("file://home/user1/router.js");
// ^^ only tow slash!
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -145,6 +162,7 @@ public class URLTest {
url = URL.valueOf("file:/home/user1/router.js");
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -155,6 +173,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("file:///d:/home/user1/router.js");
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -165,6 +184,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -179,6 +199,7 @@ public class URLTest {
assertEquals(params, url.getParameters());
url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");
+ assertURLStrDecoder(url);
assertEquals("file", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -196,6 +217,7 @@ public class URLTest {
@Test
public void test_valueOf_WithProtocolHost() throws Exception {
URL url = URL.valueOf("dubbo://10.20.130.230");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -206,6 +228,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertNull(url.getUsername());
assertNull(url.getPassword());
@@ -216,6 +239,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -226,6 +250,7 @@ public class URLTest {
assertEquals(0, url.getParameters().size());
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -237,6 +262,7 @@ public class URLTest {
assertEquals("1.0.0", url.getParameter("version"));
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -249,6 +275,7 @@ public class URLTest {
assertEquals("morgan", url.getParameter("application"));
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -266,6 +293,7 @@ public class URLTest {
@Test
public void test_valueOf_spaceSafe() throws Exception {
URL url = URL.valueOf("http://1.2.3.4:8080/path?key=value1 value2");
+ assertURLStrDecoder(url);
assertEquals("http://1.2.3.4:8080/path?key=value1 value2", url.toString());
assertEquals("value1 value2", url.getParameter("key"));
}
@@ -274,6 +302,7 @@ public class URLTest {
public void test_noValueKey() throws Exception {
URL url = URL.valueOf("http://1.2.3.4:8080/path?k0&k1=v1");
+ assertURLStrDecoder(url);
assertTrue(url.hasParameter("k0"));
// If a Key has no corresponding Value, then the Key also used as the Value.
@@ -288,38 +317,59 @@ public class URLTest {
} catch (IllegalStateException expected) {
assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage());
}
+
+ try {
+ String encodedURLStr = URL.encode("://1.2.3.4:8080/path");
+ URLStrParser.parseEncodedStr(encodedURLStr);
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", URL.decode(expected.getMessage()));
+ }
+
+ try {
+ URLStrParser.parseDecodedStr("://1.2.3.4:8080/path");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage());
+ }
}
@Test
public void test_getAddress() throws Exception {
URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url1);
assertEquals("10.20.130.230:20880", url1.getAddress());
}
@Test
public void test_getAbsolutePath() throws Exception {
URL url = new URL("p1", "1.2.2.2", 33);
+ assertURLStrDecoder(url);
assertNull(url.getAbsolutePath());
url = new URL("file", null, 90, "/home/user1/route.js");
+ assertURLStrDecoder(url);
assertEquals("/home/user1/route.js", url.getAbsolutePath());
}
@Test
public void test_equals() throws Exception {
URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url1);
Map params = new HashMap();
params.put("version", "1.0.0");
params.put("application", "morgan");
URL url2 = new URL("dubbo", "admin", "hello1234", "10.20.130.230", 20880, "context/path", params);
+ assertURLStrDecoder(url2);
assertEquals(url1, url2);
}
@Test
public void test_toString() throws Exception {
URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url1);
assertThat(url1.toString(), anyOf(
equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
@@ -329,6 +379,7 @@ public class URLTest {
@Test
public void test_toFullString() throws Exception {
URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url1);
assertThat(url1.toFullString(), anyOf(
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
@@ -338,9 +389,11 @@ public class URLTest {
@Test
public void test_set_methods() throws Exception {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
url = url.setHost("host");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -354,6 +407,7 @@ public class URLTest {
url = url.setPort(1);
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -367,6 +421,7 @@ public class URLTest {
url = url.setPath("path");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -380,6 +435,7 @@ public class URLTest {
url = url.setProtocol("protocol");
+ assertURLStrDecoder(url);
assertEquals("protocol", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -393,6 +449,7 @@ public class URLTest {
url = url.setUsername("username");
+ assertURLStrDecoder(url);
assertEquals("protocol", url.getProtocol());
assertEquals("username", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -406,6 +463,7 @@ public class URLTest {
url = url.setPassword("password");
+ assertURLStrDecoder(url);
assertEquals("protocol", url.getProtocol());
assertEquals("username", url.getUsername());
assertEquals("password", url.getPassword());
@@ -421,8 +479,10 @@ public class URLTest {
@Test
public void test_removeParameters() throws Exception {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+ assertURLStrDecoder(url);
url = url.removeParameter("version");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -438,6 +498,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
url = url.removeParameters("version", "application", "NotExistedKey");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -453,6 +514,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
url = url.removeParameters(Arrays.asList("version", "application"));
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -472,6 +534,7 @@ public class URLTest {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameter("k1", "v1");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -489,6 +552,7 @@ public class URLTest {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
URL newUrl = url.addParameter("k1", "v1");
+ assertURLStrDecoder(url);
assertSame(newUrl, url);
}
@@ -498,6 +562,7 @@ public class URLTest {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2"));
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -513,6 +578,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -528,6 +594,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "application", "xxx"));
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -543,6 +610,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameter("k1", "v1");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -557,6 +625,7 @@ public class URLTest {
url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameter("application", "xxx");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -574,12 +643,14 @@ public class URLTest {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", "v1"));
+ assertURLStrDecoder(url);
assertSame(url, newUrl);
}
{
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1&k2=v2");
URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2"));
+ assertURLStrDecoder(url);
assertSame(newUrl, url);
}
}
@@ -589,6 +660,7 @@ public class URLTest {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
url = url.addParameterIfAbsent("application", "xxx");
+ assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
@@ -639,6 +711,7 @@ public class URLTest {
@Test
public void test_Anyhost() throws Exception {
URL url = URL.valueOf("dubbo://0.0.0.0:20880");
+ assertURLStrDecoder(url);
assertEquals("0.0.0.0", url.getHost());
assertTrue(url.isAnyHost());
}
@@ -646,16 +719,19 @@ public class URLTest {
@Test
public void test_Localhost() throws Exception {
URL url = URL.valueOf("dubbo://127.0.0.1:20880");
+ assertURLStrDecoder(url);
assertEquals("127.0.0.1", url.getHost());
assertEquals("127.0.0.1:20880", url.getAddress());
assertTrue(url.isLocalHost());
url = URL.valueOf("dubbo://127.0.1.1:20880");
+ assertURLStrDecoder(url);
assertEquals("127.0.1.1", url.getHost());
assertEquals("127.0.1.1:20880", url.getAddress());
assertTrue(url.isLocalHost());
url = URL.valueOf("dubbo://localhost:20880");
+ assertURLStrDecoder(url);
assertEquals("localhost", url.getHost());
assertEquals("localhost:20880", url.getAddress());
assertTrue(url.isLocalHost());
@@ -664,21 +740,26 @@ public class URLTest {
@Test
public void test_Path() throws Exception {
URL url = new URL("dubbo", "localhost", 20880, "////path");
+ assertURLStrDecoder(url);
assertEquals("path", url.getPath());
}
@Test
public void testAddParameters() throws Exception {
URL url = URL.valueOf("dubbo://127.0.0.1:20880");
+ assertURLStrDecoder(url);
+
Map parameters = new HashMap();
parameters.put("version", null);
url.addParameters(parameters);
+ assertURLStrDecoder(url);
}
@Test
- public void testUserNamePasswordContainsAt(){
+ public void testUserNamePasswordContainsAt() {
// Test username or password contains "@"
URL url = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertEquals("ad@min", url.getUsername());
assertEquals("hello@1234", url.getPassword());
@@ -693,9 +774,10 @@ public class URLTest {
@Test
- public void testIpV6Address(){
+ public void testIpV6Address() {
// Test username or password contains "@"
URL url = URL.valueOf("ad@min111:haha@1234@2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertEquals("ad@min111", url.getUsername());
assertEquals("haha@1234", url.getPassword());
@@ -709,8 +791,9 @@ public class URLTest {
}
@Test
- public void testIpV6AddressWithScopeId(){
+ public void testIpV6AddressWithScopeId() {
URL url = URL.valueOf("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5/context/path?version=1.0.0&application=morgan");
+ assertURLStrDecoder(url);
assertNull(url.getProtocol());
assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", url.getHost());
assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", url.getAddress());
@@ -728,49 +811,67 @@ public class URLTest {
}
@Test
- public void testGetServiceKey () {
+ public void testGetServiceKey() {
URL url1 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+ assertURLStrDecoder(url1);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName", url1.getServiceKey());
URL url2 = URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName");
+ assertURLStrDecoder(url2);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName", url2.getServiceKey());
URL url3 = URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
+ assertURLStrDecoder(url3);
Assertions.assertEquals("group1/org.apache.dubbo.test.interfaceName:1.0.0", url3.getServiceKey());
URL url4 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+ assertURLStrDecoder(url4);
Assertions.assertEquals("context/path", url4.getPathKey());
URL url5 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
+ assertURLStrDecoder(url5);
Assertions.assertEquals("group1/context/path:1.0.0", url5.getPathKey());
}
@Test
public void testGetColonSeparatedKey() {
URL url1 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+ assertURLStrDecoder(url1);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:group", url1.getColonSeparatedKey());
URL url2 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&version=1.0.0");
+ assertURLStrDecoder(url2);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:", url2.getColonSeparatedKey());
URL url3 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group");
+ assertURLStrDecoder(url3);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName::group", url3.getColonSeparatedKey());
URL url4 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+ assertURLStrDecoder(url4);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", url4.getColonSeparatedKey());
URL url5 = URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName");
+ assertURLStrDecoder(url5);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", url5.getColonSeparatedKey());
URL url6 = URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName1");
+ assertURLStrDecoder(url6);
Assertions.assertEquals("org.apache.dubbo.test.interfaceName1::", url6.getColonSeparatedKey());
}
@Test
public void testValueOf() {
- URL.valueOf("10.20.130.230");
- URL.valueOf("10.20.130.230:20880");
- URL.valueOf("dubbo://10.20.130.230:20880");
- URL.valueOf("dubbo://10.20.130.230:20880/path");
+ URL url = URL.valueOf("10.20.130.230");
+ assertURLStrDecoder(url);
+
+ url = URL.valueOf("10.20.130.230:20880");
+ assertURLStrDecoder(url);
+
+ url = URL.valueOf("dubbo://10.20.130.230:20880");
+ assertURLStrDecoder(url);
+
+ url = URL.valueOf("dubbo://10.20.130.230:20880/path");
+ assertURLStrDecoder(url);
}
}
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
index 38d4cafaa1..51fa4aa8a6 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.registry.zookeeper;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.common.URLStrParser;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -44,7 +45,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
-import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SEPARATOR;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SEPARATOR_ENCODED;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
@@ -266,9 +267,8 @@ public class ZookeeperRegistry extends FailbackRegistry {
List urls = new ArrayList<>();
if (CollectionUtils.isNotEmpty(providers)) {
for (String provider : providers) {
- provider = URL.decode(provider);
- if (provider.contains(PROTOCOL_SEPARATOR)) {
- URL url = URL.valueOf(provider);
+ if (provider.contains(PROTOCOL_SEPARATOR_ENCODED)) {
+ URL url = URLStrParser.parseEncodedStr(provider);
if (UrlUtils.isMatch(consumer, url)) {
urls.add(url);
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java
index 244625805d..a18d1169cd 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java
@@ -252,7 +252,7 @@ public class DefaultFuture extends CompletableFuture