Skip to content

S3 client believes every object exists. #2445

@manarth

Description

@manarth

Describe the bug

When invoking 'doesObjectExist()` with the S3 client, the API returns TRUE for non-existing objects.

Expected Behavior

The API should return TRUE if the specified bucket and key exists.
The API should return FALSE if the specified bucket does not exist, or if the specified bucket and key do not exist.

Current Behavior

The API always returns true.

Reproduction Steps

$params = [
  'Bucket' => 'my-bucket-which-exists',
  // Non-existent key name.
  'Key' => 'sdsgfdsgregerdfdgdd',
];
$result = $s3Client->doesObjectExist($params['Bucket'], $params['Key']);

Possible Solution

The method checkExistenceWithCommand() in the S3ClientTrait executes a defined command.
If the command does not throw an exception, TRUE is returned.

    /**
     * Determines whether or not a resource exists using a command
     *
     * @param CommandInterface $command Command used to poll for the resource
     *
     * @return bool
     * @throws S3Exception|\Exception if there is an unhandled exception
     */
    private function checkExistenceWithCommand(CommandInterface $command)
    {
        try {
            $result = $this->execute($command);
            return true;
        } catch (S3Exception $e) {
            if ($e->getAwsErrorCode() == 'AccessDenied') {
                return true;
            }
            if ($e->getStatusCode() >= 500) {
                throw $e;
            }
            return false;
        }
    }

The doesObjectExist() method uses a HeadObject command to check if the bucket exists.

    /**
     * @see S3ClientInterface::doesObjectExist()
     */
    public function doesObjectExist($bucket, $key, array $options = [])
    {
        return $this->checkExistenceWithCommand(
            $this->getCommand('HeadObject', [
                    'Bucket' => $bucket,
                    'Key'    => $key
                ] + $options)
        );
    }

When executing a HeadObject command for a non-existing key, the command does not throw an Exception.
Instead, it returns the 404 response as an Aws\Result object.

class Aws\Result#2316 (2) {
  private $data =>
  array(1) {
    '@metadata' =>
    array(4) {
      'statusCode' =>
      int(404)
      'effectiveUri' =>
      string(55) "https://<redacted>.s3.amazonaws.com/sdsgfdsgregerdfdgdd"
      'headers' =>
      array(5) {
        ...
      }
      'transferStats' =>
      array(1) {
        ...
      }
    }
  }
  private $monitoringEvents =>
  array(0) {
  }
}

Additional Information/Context

The most significant end-user impact is that using mkdir() with the S3 StreamWrapper always fails, because it always believes the (pseudo)-directory exists.

Solution options may include:

  • Within checkExistenceWithCommand(), test for a 404 result (in addition to detecting an exception).
  • Within the HeadObject command, throw an Exception for non-existing objects.

SDK version used

3.222.6

Environment details (Version of PHP (php -v)? OS name and version, etc.)

PHP 7.4.29 (cli), on Ubuntu Bionic 18.04.

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.closed-for-stalenessresponse-requestedWaiting on additional info and feedback. Will move to "closing-soon" in 7 days.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions