diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index 3cbc67531..4a152355d 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -16,7 +16,6 @@ package org.springframework.cloud.sleuth; -import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -421,15 +420,30 @@ public class Span { } /** - * Represents hex string as long + * Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any + * bits higher than 64. */ public static long hexToId(String hexString) { Assert.hasText(hexString, "Can't convert empty hex string to long"); - try { - return new BigInteger(hexString, 16).longValue(); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Malformed id [" + hexString + "]", e); + int length = hexString.length(); + if (length < 1 || length > 32) throw new IllegalArgumentException("Malformed id"); + + // trim off any high bits + int i = length > 16 ? length - 16 : 0; + + long result = 0; + for (; i < length; i++) { + char c = hexString.charAt(i); + result <<= 4; + if (c >= '0' && c <= '9') { + result |= c - '0'; + } else if (c >= 'a' && c <= 'f') { + result |= c - 'a' + 10; + } else { + throw new IllegalArgumentException("Malformed id"); + } } + return result; } @Override diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java index 1021a6213..bed930500 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java @@ -52,6 +52,16 @@ public class SpanTests { then(someLong).isEqualTo(123123L); } + @Test + public void should_convert_lower_64bits_of_hex_string_to_long() throws Exception { + String hex128Bits = "463ac35c9f6413ad48485a3953bb6124"; + String lower64Bits = "48485a3953bb6124"; + + long someLong = Span.hexToId(hex128Bits); + + then(someLong).isEqualTo(Span.hexToId(lower64Bits)); + } + @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_null_string_is_to_be_converted_to_long() throws Exception { Span.hexToId(null); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java index 37e173ebc..2ab5c63af 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java @@ -275,6 +275,20 @@ public class TraceChannelInterceptorTests implements MessageHandler { then(TestSpanContextHolder.getCurrentSpan()).isNull(); } + @Test + public void downgrades128bitIdsByDroppingHighBits() { + String hex128Bits = "463ac35c9f6413ad48485a3953bb6124"; + String lower64Bits = "48485a3953bb6124"; + this.tracedChannel.send(MessageBuilder.withPayload("hi") + .setHeader(Span.TRACE_ID_NAME, hex128Bits) + .setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build()); + then(this.message).isNotNull(); + + long traceId = Span.hexToId(this.message.getHeaders() + .get(TraceMessageHeaders.TRACE_ID_NAME, String.class)); + then(traceId).isEqualTo(Span.hexToId(lower64Bits)); + } + @Configuration @EnableAutoConfiguration static class App { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java index fc660f2ad..fe1afc5df 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java @@ -93,4 +93,19 @@ public class HttpServletRequestExtractorTests { then(e).hasMessageContaining("Malformed id"); } } -} \ No newline at end of file + + @Test + public void should_downgrade_128bit_trace_id_by_dropping_high_bits() { + String hex128Bits = "463ac35c9f6413ad48485a3953bb6124"; + String lower64Bits = "48485a3953bb6124"; + + BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) + .willReturn(hex128Bits); + BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) + .willReturn(lower64Bits); + + Span span = this.extractor.joinTrace(this.request); + + then(span.getTraceId()).isEqualTo(Span.hexToId(lower64Bits)); + } +}