in agent/php/ElasticApm/Impl/HttpDistributedTracing.php [176:295]
private function parseTraceParentHeader(string $headerRawValue): ?DistributedTracingDataInternal
{
// 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01
// ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^
// || |||||||||||||||||||||||||||||||| |||||||||||||||| -- - flagsAsString
// || |||||||||||||||||||||||||||||||| ---------------- - parentId
// || -------------------------------- - trace-id
// -- - version
$headerValue = trim($headerRawValue);
$parentFunc = __FUNCTION__;
$implicitContextToLog = ['headerValue' => $headerValue];
$logParsingFailedMessage = function (
string $reason,
int $srcCodeLineNumber,
array $context = []
) use (
$parentFunc,
&$implicitContextToLog
): void {
($loggerProxy = $this->logger->ifDebugLevelEnabled($srcCodeLineNumber, $parentFunc))
&& $loggerProxy->log(
'Failed to parse ' . self::TRACE_PARENT_HEADER_NAME . ' HTTP header: '
. $reason,
array_merge($context, $implicitContextToLog)
);
};
$result = new DistributedTracingDataInternal();
$expectedNumberOfParts = 4;
$implicitContextToLog['expectedNumberOfParts'] = $expectedNumberOfParts;
/**
* limit is $expectedNumberOfParts + 1 because according to W3C spec requires parser to allow
* more than $expectedNumberOfParts in the future versions
* as long as the new parts are appended at the end after a dash
*
* @link https://www.w3.org/TR/trace-context/#versioning-of-traceparent
*/
$parts = explode(/* separator: */ '-', $headerValue, /* limit: */ $expectedNumberOfParts + 1);
$implicitContextToLog['parts'] = $parts;
if (count($parts) < $expectedNumberOfParts) {
$logParsingFailedMessage("the number of separated parts is less than expected", __LINE__);
return null;
}
$version = $parts[0];
$implicitContextToLog['version'] = $version;
/**
* @link https://www.w3.org/TR/trace-context/#version
*
* Version ff is forbidden
*/
/** @noinspection PhpStrictComparisonWithOperandsOfDifferentTypesInspection */
if (strcasecmp($version, 'ff') === 0) {
$logParsingFailedMessage('version ff is forbidden', __LINE__);
return null;
}
if (!IdValidationUtil::isValidHexNumberString($version, /* expectedSizeInBytes - 1 byte = 2 hex chars */ 1)) {
$logParsingFailedMessage('version is not a valid 2 hex characters string', __LINE__);
return null;
}
if ($version === self::TRACE_PARENT_SUPPORTED_FORMAT_VERSION && count($parts) !== $expectedNumberOfParts) {
$logParsingFailedMessage(
'there are more than expected number of separated parts for the current format version',
__LINE__
);
return null;
}
$traceId = $parts[1];
$implicitContextToLog['traceId'] = $traceId;
if (!IdValidationUtil::isValidHexNumberString($traceId, Constants::TRACE_ID_SIZE_IN_BYTES)) {
$logParsingFailedMessage(
'traceId is not a valid ' . Constants::TRACE_ID_SIZE_IN_BYTES . ' bytes hex ID',
__LINE__
);
return null;
}
if ($traceId === self::TRACE_PARENT_INVALID_TRACE_ID) {
$logParsingFailedMessage(
'traceId that is all bytes as zero (' . self::TRACE_PARENT_INVALID_TRACE_ID . ') is an invalid value',
__LINE__
);
return null;
}
$result->traceId = strtolower($traceId);
$parentId = $parts[2];
$implicitContextToLog['parentId'] = $parentId;
if (!IdValidationUtil::isValidHexNumberString($parentId, Constants::EXECUTION_SEGMENT_ID_SIZE_IN_BYTES)) {
$logParsingFailedMessage(
'parentId is not a valid ' . Constants::EXECUTION_SEGMENT_ID_SIZE_IN_BYTES . ' bytes hex ID',
__LINE__
);
return null;
}
if ($parentId === self::TRACE_PARENT_INVALID_PARENT_ID) {
$logParsingFailedMessage(
'parentId that is all bytes as zero ('
. self::TRACE_PARENT_INVALID_PARENT_ID
. ') is considered an invalid value',
__LINE__
);
return null;
}
$result->parentId = strtolower($parentId);
$flagsAsString = $parts[3];
$implicitContextToLog['flagsAsString'] = $flagsAsString;
if (!IdValidationUtil::isValidHexNumberString($flagsAsString, /* $expectedSizeInBytes */ 1)) {
$logParsingFailedMessage('flagsAsString is not a valid 2 hex characters string', __LINE__);
return null;
}
$flagsAsInt = hexdec($flagsAsString);
$result->isSampled = ($flagsAsInt & self::TRACE_PARENT_SAMPLED_FLAG) === 1;
return $result;
}