[Dubbo-Performance] Reduce memory allocation for URL parse (#5880)

This commit is contained in:
LinShunKang 2020-04-14 11:40:06 +08:00 committed by GitHub
parent 317e62f73f
commit afa2dc9a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 797 additions and 19 deletions

View File

@ -225,6 +225,8 @@ class URL implements Serializable {
}
/**
* NOTICE: This method allocate too much objects, we can use {@link URLStrParser#parseDecodedStr(String)} instead.
* <p>
* 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<String> methods = StringUtils.splitToList(methodsString, ',');
for (Map.Entry<String, String> 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);
}
}

View File

@ -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<TempBuf> 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<String, String> 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<String, String> parseDecodedParams(String str, int from) {
int len = str.length();
if (from >= len) {
return Collections.emptyMap();
}
TempBuf tempBuf = DECODE_TEMP_BUF.get();
Map<String, String> 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<String, String> 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<String, String> 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<String, String> parseEncodedParams(String str, int from) {
int len = str.length();
if (from >= len) {
return Collections.emptyMap();
}
TempBuf tempBuf = DECODE_TEMP_BUF.get();
Map<String, String> 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<String, String> 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];
}
}
}

View File

@ -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*");

View File

@ -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);
}
}

View File

@ -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 <a href=
* "https://github.com/protocolbuffers/protobuf/blob/master/java/core/src/main/java/com/google/protobuf/Utf8.java"
* >Utf8.java</a>
*/
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));
}
}
}

View File

@ -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));
}
}

View File

@ -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<String, String> params = new HashMap<String, String>();
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<String, String> parameters = new HashMap<String, String>();
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);
}
}

View File

@ -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<URL> 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);
}

View File

@ -252,7 +252,7 @@ public class DefaultFuture extends CompletableFuture<Object> {
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
+ (scan ? " by scan timer" : "") + ". start time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(nowTimestamp))) + ","
+ (sent > 0 ? " client elapsed: " + (sent - start)
+ " ms, server elapsed: " + (nowTimestamp - sent)
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "