[Dubbo-Performance] Reduce memory allocation for URL parse (#5880)
This commit is contained in:
parent
317e62f73f
commit
afa2dc9a1e
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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*");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: "
|
||||
|
|
|
|||
Loading…
Reference in New Issue