Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: track checksum calculation validation mode #3065

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/MetricsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ final class MetricsBuilder
const FLEXIBLE_CHECKSUMS_REQ_CRC64 = "W";
const FLEXIBLE_CHECKSUMS_REQ_SHA1 = "X";
const FLEXIBLE_CHECKSUMS_REQ_SHA256 = "Y";
const FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED = "Z";
const FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED = "a";
const FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED = "b";
const FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED = "c";
const CREDENTIALS_CODE = "e";
const CREDENTIALS_ENV_VARS = "g";
const CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN = "h";
Expand Down Expand Up @@ -139,6 +143,7 @@ public function identifyMetricByValueAndAppend(
'credentials' => 'appendCredentialsMetric',
'account_id_endpoint_mode' => 'appendAccountIdEndpointMode',
'account_id_endpoint' => 'appendAccountIdEndpoint',
'request_checksum_calculation' => 'appendRequestChecksumCalculationMetric',
];

$fn = $appendMetricFns[$featureGroup];
Expand Down Expand Up @@ -246,6 +251,20 @@ private function appendCredentialsMetric(
}
}

private function appendRequestChecksumCalculationMetric(
yenfryherrerafeliz marked this conversation as resolved.
Show resolved Hide resolved
string $checkSumCalculation
): void
{
static $checksumCalculationMetricMapping = [
'when_supported' => self::FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED,
'when_required' => self::FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED,
];

if (isset($checksumCalculationMetricMapping[$checkSumCalculation])) {
$this->append($checksumCalculationMetricMapping[$checkSumCalculation]);
}
}

/**
* Appends the account_id_endpoint_mode metrics based on
* the value resolved.
Expand Down Expand Up @@ -287,6 +306,103 @@ private function appendAccountIdEndpoint(string $endpoint): void
}
}

/**
* Resolves metrics from client arguments.
*
* @param array $args
*
* @return void
*/
public function resolveAndAppendFromArgs(array $args = []): void
{
static $metricsFnList = [
'appendEndpointMetric',
'appendRetryConfigMetric',
'appendResponseChecksumValidationMetric',
];
foreach ($metricsFnList as $metricFn) {
$this->{$metricFn}($args);
}
}

/**
* Appends the endpoint metric into the metrics builder,
* just if a custom endpoint was provided at client construction.
*
* @param array $args
*
* @return void
*/
private function appendEndpointMetric(array $args): void
{
if (!empty($args['endpoint_override'])) {
$this->append(MetricsBuilder::ENDPOINT_OVERRIDE);
}
}

/**
* Appends the retry mode metric into the metrics builder,
* based on the resolved retry config mode.
*
* @param array $args
*
* @return void
*/
private function appendRetryConfigMetric(array $args): void
{
$retries = $args['retries'] ?? null;
if ($retries === null) {
return;
}

$retryMode = '';
if ($retries instanceof \Aws\Retry\Configuration) {
$retryMode = $retries->getMode();
} elseif (is_array($retries)
&& isset($retries["mode"])
) {
$retryMode = $retries["mode"];
}

if ($retryMode === 'legacy') {
$this->append(
MetricsBuilder::RETRY_MODE_LEGACY
);
} elseif ($retryMode === 'standard') {
$this->append(
MetricsBuilder::RETRY_MODE_STANDARD
);
} elseif ($retryMode === 'adaptive') {
$this->append(
MetricsBuilder::RETRY_MODE_ADAPTIVE
);
}
}

/**
* Appends the provided/resolved response checksum validation mode.
*
* @param array $args
*
* @return void
*/
private function appendResponseChecksumValidationMetric(array $args): void
{
if (empty($args['response_checksum_validation'])) {
return;
}

$checksumValidation = $args['response_checksum_validation'];
static $checksumValidationMetricMapping = [
'when_supported' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED,
'when_required' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED,
];

if (isset($checksumValidationMetricMapping[$checksumValidation])) {
$this->append($checksumValidationMetricMapping[$checksumValidation]);
}
}

/**
* Validates if a metric can be appended by ensuring the total size,
* including the new metric and separator, does not exceed the limit.
Expand Down
5 changes: 5 additions & 0 deletions src/S3/ApplyChecksumMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public function __invoke(
$mode = $this->config['request_checksum_calculation']
?? self::DEFAULT_CALCULATION_MODE;

$command->getMetricsBuilder()->identifyMetricByValueAndAppend(
'request_checksum_calculation',
$mode
);

// Trigger warning if AddContentMD5 is specified for PutObject or UploadPart
$this->handleDeprecatedAddContentMD5($command);

Expand Down
58 changes: 3 additions & 55 deletions src/UserAgentMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ class UserAgentMiddleware
'getMetrics'
];

static $metricsFnList = [
'appendEndpointMetric',
'appendRetryConfigMetric',
];

/** @var callable */
private $nextHandler;

Expand Down Expand Up @@ -257,61 +252,14 @@ private function getAppId(): string
*/
private function getMetrics(): string
{
foreach (self::$metricsFnList as $fn) {
$this->{$fn}();
}

// Resolve first metrics related to client arguments.
$this->metricsBuilder->resolveAndAppendFromArgs($this->args);
// Build the metrics.
$metricsEncoded = $this->metricsBuilder->build();
if (empty($metricsEncoded)) {
return "";
}

return "m/" . $metricsEncoded;
}

/**
* Appends the endpoint metric into the metrics builder,
* just if a custom endpoint was provided at client construction.
*/
private function appendEndpointMetric(): void
{
if (!empty($this->args['endpoint_override'])) {
$this->metricsBuilder->append(MetricsBuilder::ENDPOINT_OVERRIDE);
}
}

/**
* Appends the retry mode metric into the metrics builder,
* based on the resolved retry config mode.
*/
private function appendRetryConfigMetric(): void
{
$retries = $this->args['retries'] ?? null;
if ($retries === null) {
return;
}

$retryMode = '';
if ($retries instanceof \Aws\Retry\Configuration) {
$retryMode = $retries->getMode();
} elseif (is_array($retries)
&& isset($retries["mode"])
) {
$retryMode = $retries["mode"];
}

if ($retryMode === 'legacy') {
$this->metricsBuilder->append(
MetricsBuilder::RETRY_MODE_LEGACY
);
} elseif ($retryMode === 'standard') {
$this->metricsBuilder->append(
MetricsBuilder::RETRY_MODE_STANDARD
);
} elseif ($retryMode === 'adaptive') {
$this->metricsBuilder->append(
MetricsBuilder::RETRY_MODE_ADAPTIVE
);
}
}
}
73 changes: 73 additions & 0 deletions tests/MetricsBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,77 @@ function (
$command = new Command('Buzz', []);
$handlerFn($command);
}

/**
* Tests resolves and appends metrics from client args.
*
* @param array $args
* @param string $expectedMetrics
*
* @dataProvider resolveAndAppendFromArgsProvider
*
* @return void
*/
public function testResolveAndAppendFromArgs(
array $args,
string $expectedMetrics,
) {
$builder = new MetricsBuilder();
$builder->resolveAndAppendFromArgs($args);

$this->assertEquals($expectedMetrics, $builder->build());
}

/**
* Provider for metrics that resolves from client arguments.
*
* @return array[]
*/
public function resolveAndAppendFromArgsProvider(): array
{
return [
'endpoint_override' => [
'args' => [
'endpoint_override' => true
],
'expectedMetrics' => MetricsBuilder::ENDPOINT_OVERRIDE,
],
'retry_config_metric_legacy' => [
'args' => [
'retries' => [
'mode' => 'legacy'
]
],
'expectedMetrics' => MetricsBuilder::RETRY_MODE_LEGACY,
],
'retry_config_metric_adaptive' => [
'args' => [
'retries' => [
'mode' => 'adaptive'
]
],
'expectedMetrics' => MetricsBuilder::RETRY_MODE_ADAPTIVE,
],
'retry_config_metric_standard' => [
'args' => [
'retries' => [
'mode' => 'standard'
]
],
'expectedMetrics' => MetricsBuilder::RETRY_MODE_STANDARD,
],
'response_checksum_validation_when_supported' => [
'args' => [
'response_checksum_validation' => 'when_supported'
],
'expectedMetrics' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED,
],
'response_checksum_validation_when_required' => [
'args' => [
'response_checksum_validation' => 'when_required'
],
'expectedMetrics' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED,
]
];
}
}
80 changes: 80 additions & 0 deletions tests/UserAgentMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1557,4 +1557,84 @@ public function testUserAgentCaptureCredentialsSSOLegacyMetric()
]);
$s3Client->listBuckets();
}

/**
* Tests user agent captures the flexible checksum calculation metric.
*
* @return void
*/
public function testUserAgentCaptureFlexibleChecksumCalculationMetric()
{
$checksumCalculationMetrics = [
'when_supported' => MetricsBuilder::FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED,
'when_required' => MetricsBuilder::FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED
];
foreach ($checksumCalculationMetrics as $config => $checksumCalculationMetric) {
$s3Client = new S3Client([
'region' => 'us-west-2',
'api_provider' => ApiProvider::filesystem(__DIR__ . '/S3/fixtures'),
'request_checksum_calculation' => $config,
'http_handler' => function (RequestInterface $request)
use ($checksumCalculationMetric) {
$metrics = $this->getMetricsAsArray($request);

$this->assertTrue(
in_array($checksumCalculationMetric, $metrics)
);

return new Response(
200,
[],
'<?xml version="1.0" encoding="UTF-8"?><Node></Node>'
);
}
]);
$s3Client->putObject([
'Bucket' => 'foo',
'Key' => 'foo',
'Body' => 'Test body',
'ChecksumAlgorithm' => 'crc32'
]);
}
}

/**
* Tests user agent captures the flexible checksum validation metric.
*
* @return void
*/
public function testUserAgentCaptureFlexibleChecksumValidationMetric()
{
$checksumCalculationMetrics = [
'when_supported' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED,
'when_required' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED
];
foreach ($checksumCalculationMetrics as $config => $checksumCalculationMetric) {
$s3Client = new S3Client([
'region' => 'us-west-2',
'api_provider' => ApiProvider::filesystem(__DIR__ . '/S3/fixtures'),
'response_checksum_validation' => $config,
'http_handler' => function (RequestInterface $request)
use ($checksumCalculationMetric) {
$metrics = $this->getMetricsAsArray($request);

$this->assertTrue(
in_array($checksumCalculationMetric, $metrics)
);

return new Response(
200,
[],
'<?xml version="1.0" encoding="UTF-8"?><Node></Node>'
);
}
]);
$s3Client->putObject([
'Bucket' => 'foo',
'Key' => 'foo',
'Body' => 'Test body',
'ChecksumAlgorithm' => 'crc32'
]);
}
}
}