diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index 79aadeff..c319bc57 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -26,6 +26,14 @@ class S3 extends Device const METHOD_TRACE = 'TRACE'; + const HTTP_VERSION_1_1 = CURL_HTTP_VERSION_1_1; + + const HTTP_VERSION_2_0 = CURL_HTTP_VERSION_2_0; + + const HTTP_VERSION_2 = CURL_HTTP_VERSION_2; + + const HTTP_VERSION_1_0 = CURL_HTTP_VERSION_1_0; + /** * AWS Regions constants */ @@ -128,7 +136,8 @@ class S3 extends Device * @var array */ protected array $headers = [ - 'host' => '', 'date' => '', + 'host' => '', + 'date' => '', 'content-md5' => '', 'content-type' => '', ]; @@ -138,6 +147,13 @@ class S3 extends Device */ protected array $amzHeaders; + /** + * Http version + * + * @var int|null + */ + protected ?int $curlHttpVersion = null; + /** * S3 Constructor * @@ -148,7 +164,7 @@ class S3 extends Device * @param string $region * @param string $acl */ - public function __construct(string $root, string $accessKey, string $secretKey, string $bucket, string $region = self::US_EAST_1, string $acl = self::ACL_PRIVATE) + public function __construct(string $root, string $accessKey, string $secretKey, string $bucket, string $region = self::US_EAST_1, string $acl = self::ACL_PRIVATE, $endpointUrl = '') { $this->accessKey = $accessKey; $this->secretKey = $secretKey; @@ -158,10 +174,14 @@ public function __construct(string $root, string $accessKey, string $secretKey, $this->acl = $acl; $this->amzHeaders = []; - $host = match ($region) { - self::CN_NORTH_1, self::CN_NORTH_4, self::CN_NORTHWEST_1 => $bucket.'.s3.'.$region.'.amazonaws.cn', - default => $bucket.'.s3.'.$region.'.amazonaws.com' - }; + if (! empty($endpointUrl)) { + $host = $bucket.'.'.$endpointUrl; + } else { + $host = match ($region) { + self::CN_NORTH_1, self::CN_NORTH_4, self::CN_NORTHWEST_1 => $bucket.'.s3.'.$region.'.amazonaws.cn', + default => $bucket.'.s3.'.$region.'.amazonaws.com' + }; + } $this->headers['host'] = $host; } @@ -208,6 +228,20 @@ public function getPath(string $filename, string $prefix = null): string return $this->getRoot().DIRECTORY_SEPARATOR.$filename; } + /** + * Set http version + * + * + * @param int|null $httpVersion + * @return self + */ + public function setHttpVersion(?int $httpVersion): self + { + $this->curlHttpVersion = $httpVersion; + + return $this; + } + /** * Upload. * @@ -483,7 +517,7 @@ public function delete(string $path, bool $recursive = false): bool * * @throws Exception */ - private function listObjects(string $prefix = '', int $maxKeys = self::MAX_PAGE_SIZE, string $continuationToken = ''): array + protected function listObjects(string $prefix = '', int $maxKeys = self::MAX_PAGE_SIZE, string $continuationToken = ''): array { if ($maxKeys > self::MAX_PAGE_SIZE) { throw new Exception('Cannot list more than '.self::MAX_PAGE_SIZE.' objects'); @@ -494,6 +528,9 @@ private function listObjects(string $prefix = '', int $maxKeys = self::MAX_PAGE_ $this->headers['content-type'] = 'text/plain'; $this->headers['content-md5'] = \base64_encode(md5('', true)); + unset($this->amzHeaders['x-amz-content-sha256']); + unset($this->amzHeaders['x-amz-acl']); + $parameters = [ 'list-type' => 2, 'prefix' => $prefix, @@ -765,8 +802,10 @@ private function getSignatureV4(string $method, string $uri, array $parameters = // stringToSign $stringToSignStr = \implode("\n", [ - $algorithm, $this->amzHeaders['x-amz-date'], - \implode('/', $credentialScope), \hash('sha256', $amzPayloadStr), + $algorithm, + $this->amzHeaders['x-amz-date'], + \implode('/', $credentialScope), + \hash('sha256', $amzPayloadStr), ]); // Make Signature @@ -797,7 +836,7 @@ private function getSignatureV4(string $method, string $uri, array $parameters = * * @throws \Exception */ - private function call(string $method, string $uri, string $data = '', array $parameters = [], bool $decode = true) + protected function call(string $method, string $uri, string $data = '', array $parameters = [], bool $decode = true) { $uri = $this->getAbsolutePath($uri); $url = 'https://'.$this->headers['host'].$uri.'?'.\http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); @@ -837,6 +876,11 @@ private function call(string $method, string $uri, string $data = '', array $par \curl_setopt($curl, CURLOPT_HTTPHEADER, $httpHeaders); \curl_setopt($curl, CURLOPT_HEADER, false); \curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + + if ($this->curlHttpVersion != null) { + \curl_setopt($curl, CURLOPT_HTTP_VERSION, $this->curlHttpVersion); + } + \curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($curl, string $data) use ($response) { $response->body .= $data; diff --git a/tests/Storage/Device/BackblazeTest.php b/tests/Storage/Device/BackblazeTest.php index 91ce2f8f..d2d277b4 100644 --- a/tests/Storage/Device/BackblazeTest.php +++ b/tests/Storage/Device/BackblazeTest.php @@ -12,7 +12,7 @@ protected function init(): void $this->root = '/root'; $key = $_SERVER['BACKBLAZE_ACCESS_KEY'] ?? ''; $secret = $_SERVER['BACKBLAZE_SECRET'] ?? ''; - $bucket = 'utopia-storage-test'; + $bucket = 'utopia-storage-test-new'; $this->object = new Backblaze($this->root, $key, $secret, $bucket, Backblaze::US_WEST_004, Backblaze::ACL_PRIVATE); }