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
This commit is contained in:
parent
408a777d9d
commit
bf38e43d3a
|
|
@ -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 "";
|
||||
}
|
||||
return new String(DECODER.decode(in), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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 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) {
|
||||
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue