未验证 提交 bf38e43d 编写于 作者: K kezhenxu94 提交者: GitHub

Clean up legacy v1 header logic, use built-in Base64 since JDK8 (#4307)

### Motivation:

Codes clean up

### Modifications:

- Remove v1 header, follow up #4244

- Use built-in Base64 class (since JDK8)

### Result:

- No more legacy v1 headers

- No unnecessary codes concerns by using built-in ability
上级 408a777d
......@@ -17,172 +17,24 @@
*/
package org.apache.skywalking.apm.agent.core.base64;
import java.io.UnsupportedEncodingException;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import java.nio.charset.StandardCharsets;
/**
* Copied from {@code zipkin.internal.Base64}, adapted from {@code okio.Base64} as JRE 6 doesn't have a base64Url
* encoder.
*
* @author okio cited the original author as Alexander Y. Kleymenov
* A wrapper of {@link java.util.Base64} with convenient conversion methods between {@code byte[]} and {@code String}
*/
public final class Base64 {
private static final ILog logger = LogManager.getLogger(Base64.class);
private static final java.util.Base64.Decoder DECODER = java.util.Base64.getDecoder();
private static final java.util.Base64.Encoder ENCODER = java.util.Base64.getEncoder();
private Base64() {
}
public static String decode2UTFString(String in) {
try {
return new String(decode(in), "utf-8");
} catch (UnsupportedEncodingException e) {
logger.error(e, "Can't decode BASE64 text {}", in);
return "";
}
}
public static byte[] decode(String in) {
// Ignore trailing '=' padding and whitespace from the input.
int limit = in.length();
for (; limit > 0; limit--) {
char c = in.charAt(limit - 1);
if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') {
break;
}
}
// If the input includes whitespace, this output array will be longer than necessary.
byte[] out = new byte[(int)(limit * 6L / 8L)];
int outCount = 0;
int inCount = 0;
int word = 0;
for (int pos = 0; pos < limit; pos++) {
char c = in.charAt(pos);
int bits;
if (c >= 'A' && c <= 'Z') {
// char ASCII value
// A 65 0
// Z 90 25 (ASCII - 65)
bits = c - 65;
} else if (c >= 'a' && c <= 'z') {
// char ASCII value
// a 97 26
// z 122 51 (ASCII - 71)
bits = c - 71;
} else if (c >= '0' && c <= '9') {
// char ASCII value
// 0 48 52
// 9 57 61 (ASCII + 4)
bits = c + 4;
} else if (c == '+' || c == '-') {
bits = 62;
} else if (c == '/' || c == '_') {
bits = 63;
} else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
continue;
} else {
return null;
}
// Append this char's 6 bits to the word.
word = (word << 6) | (byte)bits;
// For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes.
inCount++;
if (inCount % 4 == 0) {
out[outCount++] = (byte)(word >> 16);
out[outCount++] = (byte)(word >> 8);
out[outCount++] = (byte)word;
}
}
int lastWordChars = inCount % 4;
if (lastWordChars == 1) {
// We read 1 char followed by "===". But 6 bits is a truncated byte! Fail.
return null;
} else if (lastWordChars == 2) {
// We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits.
word = word << 12;
out[outCount++] = (byte)(word >> 16);
} else if (lastWordChars == 3) {
// We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits.
word = word << 6;
out[outCount++] = (byte)(word >> 16);
out[outCount++] = (byte)(word >> 8);
}
// If we sized our out array perfectly, we're done.
if (outCount == out.length)
return out;
// Copy the decoded bytes to a new, right-sized array.
byte[] prefix = new byte[outCount];
System.arraycopy(out, 0, prefix, 0, outCount);
return prefix;
return new String(DECODER.decode(in), StandardCharsets.UTF_8);
}
private static final byte[] MAP = new byte[] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '+', '/'
};
private static final byte[] URL_MAP = new byte[] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '-', '_'
};
public static String encode(String text) {
try {
return encode(text.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
logger.error(e, "Can't encode {} in BASE64", text);
return "";
}
return ENCODER.encodeToString(text.getBytes(StandardCharsets.UTF_8));
}
public static String encode(byte[] in) {
return encode(in, MAP);
}
public static String encodeUrl(byte[] in) {
return encode(in, URL_MAP);
}
private static String encode(byte[] in, byte[] map) {
int length = (in.length + 2) / 3 * 4;
byte[] out = new byte[length];
int index = 0, end = in.length - in.length % 3;
for (int i = 0; i < end; i += 3) {
out[index++] = map[(in[i] & 0xff) >> 2];
out[index++] = map[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)];
out[index++] = map[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)];
out[index++] = map[in[i + 2] & 0x3f];
}
switch (in.length % 3) {
case 1:
out[index++] = map[(in[end] & 0xff) >> 2];
out[index++] = map[(in[end] & 0x03) << 4];
out[index++] = '=';
out[index++] = '=';
break;
case 2:
out[index++] = map[(in[end] & 0xff) >> 2];
out[index++] = map[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)];
out[index++] = map[(in[end + 1] & 0x0f) << 2];
out[index++] = '=';
break;
}
try {
return new String(out, "US-ASCII");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
}
......@@ -24,7 +24,6 @@ import org.apache.skywalking.apm.agent.core.base64.Base64;
import org.apache.skywalking.apm.agent.core.context.ids.DistributedTraceId;
import org.apache.skywalking.apm.agent.core.context.ids.ID;
import org.apache.skywalking.apm.agent.core.context.ids.PropagatedTraceId;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.dictionary.DictionaryUtil;
import org.apache.skywalking.apm.util.StringUtil;
......@@ -35,9 +34,6 @@ import org.apache.skywalking.apm.util.StringUtil;
* Created by wusheng on 2017/2/17.
*/
public class ContextCarrier implements Serializable {
/**
* {@link TraceSegment#traceSegmentId}
*/
private ID traceSegmentId;
/**
......@@ -97,9 +93,8 @@ public class ContextCarrier implements Serializable {
Base64.encode(this.getPeerHost()),
Base64.encode(this.getEntryEndpointName()),
Base64.encode(this.getParentEndpointName()));
} else {
return "";
}
return "";
}
/**
......@@ -108,53 +103,36 @@ public class ContextCarrier implements Serializable {
* @param text carries {@link #traceSegmentId} and {@link #spanId}, with '|' split.
*/
ContextCarrier deserialize(String text, HeaderVersion version) {
if (text != null) {
// if this carrier is initialized by v1 or v2, don't do deserialize again for performance.
if (this.isValid(HeaderVersion.v1) || this.isValid(HeaderVersion.v2)) {
return this;
}
if (HeaderVersion.v1.equals(version)) {
String[] parts = text.split("\\|", 8);
if (parts.length == 8) {
try {
this.traceSegmentId = new ID(parts[0]);
this.spanId = Integer.parseInt(parts[1]);
this.parentServiceInstanceId = Integer.parseInt(parts[2]);
this.entryServiceInstanceId = Integer.parseInt(parts[3]);
this.peerHost = parts[4];
this.entryEndpointName = parts[5];
this.parentEndpointName = parts[6];
this.primaryDistributedTraceId = new PropagatedTraceId(parts[7]);
} catch (NumberFormatException e) {
}
}
} else if (HeaderVersion.v2.equals(version)) {
String[] parts = text.split("\\-", 9);
if (parts.length == 9) {
try {
// parts[0] is sample flag, always trace if header exists.
this.primaryDistributedTraceId = new PropagatedTraceId(Base64.decode2UTFString(parts[1]));
this.traceSegmentId = new ID(Base64.decode2UTFString(parts[2]));
this.spanId = Integer.parseInt(parts[3]);
this.parentServiceInstanceId = Integer.parseInt(parts[4]);
this.entryServiceInstanceId = Integer.parseInt(parts[5]);
this.peerHost = Base64.decode2UTFString(parts[6]);
this.entryEndpointName = Base64.decode2UTFString(parts[7]);
this.parentEndpointName = Base64.decode2UTFString(parts[8]);
} catch (NumberFormatException e) {
}
if (text == null) {
return this;
}
// if this carrier is initialized by v2, don't do deserialize again for performance.
if (this.isValid(HeaderVersion.v2)) {
return this;
}
if (HeaderVersion.v2 == version) {
String[] parts = text.split("-", 9);
if (parts.length == 9) {
try {
// parts[0] is sample flag, always trace if header exists.
this.primaryDistributedTraceId = new PropagatedTraceId(Base64.decode2UTFString(parts[1]));
this.traceSegmentId = new ID(Base64.decode2UTFString(parts[2]));
this.spanId = Integer.parseInt(parts[3]);
this.parentServiceInstanceId = Integer.parseInt(parts[4]);
this.entryServiceInstanceId = Integer.parseInt(parts[5]);
this.peerHost = Base64.decode2UTFString(parts[6]);
this.entryEndpointName = Base64.decode2UTFString(parts[7]);
this.parentEndpointName = Base64.decode2UTFString(parts[8]);
} catch (NumberFormatException ignored) {
}
} else {
throw new IllegalArgumentException("Unimplemented header version." + version);
}
}
return this;
}
public boolean isValid() {
return isValid(HeaderVersion.v2) || isValid(HeaderVersion.v1);
return isValid(HeaderVersion.v2);
}
/**
......@@ -163,17 +141,7 @@ public class ContextCarrier implements Serializable {
* @return true for unbroken {@link ContextCarrier} or no-initialized. Otherwise, false;
*/
boolean isValid(HeaderVersion version) {
if (HeaderVersion.v1.equals(version)) {
return traceSegmentId != null
&& traceSegmentId.isValid()
&& getSpanId() > -1
&& parentServiceInstanceId != DictionaryUtil.nullValue()
&& entryServiceInstanceId != DictionaryUtil.nullValue()
&& !StringUtil.isEmpty(peerHost)
&& !StringUtil.isEmpty(entryEndpointName)
&& !StringUtil.isEmpty(parentEndpointName)
&& primaryDistributedTraceId != null;
} else if (HeaderVersion.v2.equals(version)) {
if (HeaderVersion.v2 == version) {
return traceSegmentId != null
&& traceSegmentId.isValid()
&& getSpanId() > -1
......@@ -181,9 +149,8 @@ public class ContextCarrier implements Serializable {
&& entryServiceInstanceId != DictionaryUtil.nullValue()
&& !StringUtil.isEmpty(peerHost)
&& primaryDistributedTraceId != null;
} else {
throw new IllegalArgumentException("Unimplemented header version." + version);
}
return false;
}
public String getEntryEndpointName() {
......@@ -267,6 +234,6 @@ public class ContextCarrier implements Serializable {
}
public enum HeaderVersion {
v1, v2
v2
}
}
......@@ -22,6 +22,7 @@ package org.apache.skywalking.apm.agent.core.context;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.List;
import java.util.Objects;
import org.apache.skywalking.apm.agent.core.conf.RemoteDownstreamConfig;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
......@@ -80,7 +81,7 @@ public class ContextManagerTest {
@Test
public void createSpanWithInvalidateContextCarrier() {
ContextCarrier contextCarrier = new ContextCarrier().deserialize("#AQA=#AQA=4WcWe0tQNQA=|1|#127.0.0.1:8080|#/testEntrySpan|#/testEntrySpan|#AQA=#AQA=Et0We0tQNQA=", ContextCarrier.HeaderVersion.v1);
ContextCarrier contextCarrier = new ContextCarrier();
AbstractSpan firstEntrySpan = ContextManager.createEntrySpan("/testEntrySpan", contextCarrier);
firstEntrySpan.setComponent(ComponentsDefine.TOMCAT);
......@@ -94,7 +95,7 @@ public class ContextManagerTest {
assertNull(actualSegment.getRefs());
List<AbstractTracingSpan> spanList = SegmentHelper.getSpan(actualSegment);
assertThat(spanList.size(), is(1));
assertThat(Objects.requireNonNull(spanList).size(), is(1));
AbstractTracingSpan actualEntrySpan = spanList.get(0);
assertThat(actualEntrySpan.getOperationName(), is("/testEntrySpan"));
......@@ -104,7 +105,7 @@ public class ContextManagerTest {
@Test
public void createMultipleEntrySpan() {
ContextCarrier contextCarrier = new ContextCarrier().deserialize("1.2343.234234234|1|1|1|#127.0.0.1:8080|#/portal/|#/testEntrySpan|1.2343.234234234", ContextCarrier.HeaderVersion.v1);
ContextCarrier contextCarrier = new ContextCarrier().deserialize("1-MS4yMzQzLjIzNDIzNDIzNA==-MS4yMzQzLjIzNDIzNDIzNA==-1-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWwv-Iy90ZXN0RW50cnlTcGFu", ContextCarrier.HeaderVersion.v2);
assertTrue(contextCarrier.isValid());
AbstractSpan firstEntrySpan = ContextManager.createEntrySpan("/testFirstEntry", contextCarrier);
......@@ -195,7 +196,7 @@ public class ContextManagerTest {
assertNull(actualSegment.getRefs());
List<AbstractTracingSpan> spanList = SegmentHelper.getSpan(actualSegment);
assertThat(spanList.size(), is(2));
assertThat(Objects.requireNonNull(spanList).size(), is(2));
AbstractTracingSpan actualFirstExitSpan = spanList.get(0);
assertThat(actualFirstExitSpan.getOperationName(), is("/testFirstExit"));
......@@ -227,7 +228,7 @@ public class ContextManagerTest {
@Test
public void testTransform() throws InvalidProtocolBufferException {
ContextCarrier contextCarrier = new ContextCarrier().deserialize("1.234.1983829|3|1|1|#127.0.0.1:8080|#/portal/|#/testEntrySpan|1.2343.234234234", ContextCarrier.HeaderVersion.v1);
ContextCarrier contextCarrier = new ContextCarrier().deserialize("1-MS4yMzQzLjIzNDIzNDIzNA==-MS4yMzQuMTk4MzgyOQ==-3-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWwv-Iy90ZXN0RW50cnlTcGFu", ContextCarrier.HeaderVersion.v2);
assertTrue(contextCarrier.isValid());
AbstractSpan firstEntrySpan = ContextManager.createEntrySpan("/testFirstEntry", contextCarrier);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册