You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/02/02 09:45:16 UTC

[skywalking] branch master updated: Clean up legacy v1 header logic, use built-in Base64 since JDK8 (#4307)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new bf38e43  Clean up legacy v1 header logic, use built-in Base64 since JDK8 (#4307)
bf38e43 is described below

commit bf38e43d3aadeec93a0b30f9b92e2226ff9e953c
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Sun Feb 2 17:45:05 2020 +0800

    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
---
 .../skywalking/apm/agent/core/base64/Base64.java   | 160 +--------------------
 .../apm/agent/core/context/ContextCarrier.java     |  87 ++++-------
 .../apm/agent/core/context/ContextManagerTest.java |  11 +-
 3 files changed, 39 insertions(+), 219 deletions(-)

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 2f17909..11d8d65 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 33d4aed..03eaf05 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 66b435e..6e048d8 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<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);