diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/base64/Base64.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/base64/Base64.java index 2f179097ccc5c8847497a5a275a981ddc204f975..11d8d6563c6b85cdeb83273751473dea825373e6 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/base64/Base64.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/base64/Base64.java @@ -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); - } - } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextCarrier.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextCarrier.java index 33d4aedd1b0d86680564c5ecd6db20fcbb390b95..03eaf0514a0bca463b0dec6c5901443c811fce49 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextCarrier.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextCarrier.java @@ -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 } } diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerTest.java index 66b435ec2a890098afc9a623abe9c3192d5202ef..6e048d8909279b0a3dc725e445b3769bbc85e978 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerTest.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContextManagerTest.java @@ -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 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 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);