This commit is contained in:
2026-08-25 12:30:07 +08:00
parent 12dad6673d
commit 1382902f86
16 changed files with 687 additions and 19 deletions
@@ -0,0 +1,84 @@
package com.baiyue.common.web;
import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Strict W3C {@code traceparent} version 00 value.
*
* <p>This type deliberately accepts only the currently frozen version-00 shape. Unsupported,
* ambiguous or malformed input must start a new trace instead of being partially trusted.</p>
*/
public record TraceParent(String traceId, String parentId, String traceFlags) {
public static final String HEADER = "traceparent";
public static final int HEADER_LENGTH = 55;
private static final Pattern VERSION_00 = Pattern.compile(
"00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})");
private static final Pattern LEGACY_TRACE_ID = Pattern.compile("[0-9A-Fa-f]{32}");
private static final String ZERO_TRACE_ID = "0".repeat(32);
private static final String ZERO_PARENT_ID = "0".repeat(16);
private static final SecureRandom RANDOM = new SecureRandom();
private static final HexFormat HEX = HexFormat.of();
public TraceParent {
if (!validTraceId(traceId) || !validParentId(parentId)
|| traceFlags == null || !traceFlags.matches("[0-9a-f]{2}")) {
throw new IllegalArgumentException("Invalid W3C traceparent components");
}
}
/** Parses one exact, lowercase W3C version-00 header value. */
public static Optional<TraceParent> parse(String value) {
if (value == null || value.length() != HEADER_LENGTH) {
return Optional.empty();
}
Matcher matcher = VERSION_00.matcher(value);
if (!matcher.matches() || !validTraceId(matcher.group(1)) || !validParentId(matcher.group(2))) {
return Optional.empty();
}
return Optional.of(new TraceParent(matcher.group(1), matcher.group(2), matcher.group(3)));
}
/** Creates the local inbound context for a valid remote parent. */
public static TraceParent childOf(TraceParent remoteParent) {
return new TraceParent(remoteParent.traceId(), randomNonZero(8), remoteParent.traceFlags());
}
/** Creates a new root context, optionally reusing a valid legacy trace ID. */
public static TraceParent newRoot(String legacyTraceId) {
String traceId = validLegacyTraceId(legacyTraceId)
? legacyTraceId.toLowerCase(java.util.Locale.ROOT) : randomNonZero(16);
return new TraceParent(traceId, randomNonZero(8), "00");
}
public static boolean validLegacyTraceId(String value) {
return value != null && LEGACY_TRACE_ID.matcher(value).matches()
&& !ZERO_TRACE_ID.equalsIgnoreCase(value);
}
public String value() {
return "00-" + traceId + '-' + parentId + '-' + traceFlags;
}
private static boolean validTraceId(String value) {
return value != null && value.matches("[0-9a-f]{32}") && !ZERO_TRACE_ID.equals(value);
}
private static boolean validParentId(String value) {
return value != null && value.matches("[0-9a-f]{16}") && !ZERO_PARENT_ID.equals(value);
}
private static String randomNonZero(int byteCount) {
byte[] bytes = new byte[byteCount];
String value;
do {
RANDOM.nextBytes(bytes);
value = HEX.formatHex(bytes);
} while (value.chars().allMatch(character -> character == '0'));
return value;
}
}