Skip to content
Open
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
32 changes: 27 additions & 5 deletions CdnEngine_S3_Compatible.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,37 @@ class CdnEngine_S3_Compatible extends CdnEngine_Base {
public function __construct( $config = array() ) {
$config = array_merge(
array(
'key' => '',
'secret' => '',
'bucket' => '',
'cname' => array(),
'key' => '',
'secret' => '',
'bucket' => '',
'cname' => array(),
'ssl' => 'auto',
'storage_type' => 'auto',
),
$config
);

$this->_s3 = new \S3Compatible( $config['key'], $config['secret'], false, $config['api_host'] );
// Determine SSL usage from config
$useSSL = false;
if ( isset( $config['ssl'] ) ) {
if ( $config['ssl'] === 'enabled' ) {
$useSSL = true;
} elseif ( $config['ssl'] === 'auto' ) {
// Auto-detect: use SSL if endpoint contains port 443
$api_host = isset( $config['api_host'] ) ? $config['api_host'] : '';
$useSSL = ( strpos( $api_host, ':443' ) !== false );
}
}

// Extract hostname and port from api_host
$api_host = isset( $config['api_host'] ) ? $config['api_host'] : '';
// Pass full endpoint with port - S3Compatible will extract port for Host header
$endpoint = $api_host;

// Pass storage_type to S3Compatible for URL format determination
$storage_type = isset( $config['storage_type'] ) ? $config['storage_type'] : 'auto';

$this->_s3 = new \S3Compatible( $config['key'], $config['secret'], $useSSL, $endpoint, '', $storage_type );
$this->_s3->setSignatureVersion( 'v2' );

parent::__construct( $config );
Expand Down
3 changes: 2 additions & 1 deletion Cdn_Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ public function get_cdn() {
'cname' => $c->get_array( 'cdn.s3.cname' ),
'ssl' => $c->get_string( 'cdn.s3.ssl' ),
'compression' => $compression,
'api_host' => $c->get_string( 'cdn.s3_compatible.api_host' ),
'api_host' => $c->get_string( 'cdn.s3_compatible.api_host' ),
'storage_type' => $c->get_string( 'cdn.s3_compatible.storage_type' ),
);
break;

Expand Down
4 changes: 4 additions & 0 deletions ConfigKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,10 @@
'type' => 'string',
'default' => 'auto',
),
'cdn.s3_compatible.storage_type' => array(
'type' => 'string',
'default' => 'auto',
),
'cdn.cf.key' => array(
'type' => 'string',
'default' => '',
Expand Down
19 changes: 19 additions & 0 deletions inc/options/cdn/s3_compatible.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,25 @@
'description' => esc_html__( 'Host of API endpoint, comptabile with Amazon S3 API', 'w3-total-cache' ),
)
);
?>
<tr>
<th>
<label for="cdn_s3_compatible_storage_type">
<?php esc_html_e( 'Storage type:', 'w3-total-cache' ); ?>
</label>
</th>
<td>
<select id="cdn_s3_compatible_storage_type" name="cdn__s3_compatible__storage_type" <?php Util_Ui::sealing_disabled( 'cdn.' ); ?>>
<option value="auto"<?php selected( $this->_config->get_string( 'cdn.s3_compatible.storage_type' ), 'auto' ); ?>><?php esc_html_e( 'Auto (detect from endpoint)', 'w3-total-cache' ); ?></option>
<option value="aws"<?php selected( $this->_config->get_string( 'cdn.s3_compatible.storage_type' ), 'aws' ); ?>><?php esc_html_e( 'AWS S3 compatible (virtual-hosted-style)', 'w3-total-cache' ); ?></option>
<option value="minio"<?php selected( $this->_config->get_string( 'cdn.s3_compatible.storage_type' ), 'minio' ); ?>><?php esc_html_e( 'MinIO (path-style)', 'w3-total-cache' ); ?></option>
</select>
<p class="description">
<?php esc_html_e( 'Select storage type to use appropriate URL format. AWS S3 uses virtual-hosted-style URLs (bucket.host), MinIO uses path-style URLs (host/bucket/path).', 'w3-total-cache' ); ?>
</p>
</td>
</tr>
<?php
Util_Ui::config_item(
array(
'key' => 'cdn.s3.key',
Expand Down
85 changes: 80 additions & 5 deletions lib/S3Compatible.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ class S3Compatible
*/
public static $endpoint = 's3.amazonaws.com';

/**
* Endpoint port (if specified)
*
* @var string
* @access public
* @static
*/
public static $endpointPort = '';

/**
* Storage type: 'auto', 'aws', or 'minio'
*
* @var string
* @access public
* @static
*/
public static $storageType = 'auto';

/**
* AWS Region
*
Expand Down Expand Up @@ -195,15 +213,28 @@ class S3Compatible
* @param string $secretKey Secret key
* @param boolean $useSSL Enable SSL
* @param string $endpoint Amazon URI
* @param string $region AWS region
* @param string $storageType Storage type: 'auto', 'aws', or 'minio'
* @return void
*/
public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com', $region = '')
public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com', $region = '', $storageType = 'auto')
{
if ($accessKey !== null && $secretKey !== null)
self::setAuth($accessKey, $secretKey);
self::$useSSL = $useSSL;
self::$endpoint = $endpoint;

// Extract port from endpoint if present
if (strpos($endpoint, ':') !== false) {
$parts = explode(':', $endpoint, 2);
self::$endpoint = $parts[0];
self::$endpointPort = ':' . $parts[1];
} else {
self::$endpoint = $endpoint;
self::$endpointPort = '';
}

self::$region = $region;
self::$storageType = $storageType;
}


Expand All @@ -215,7 +246,26 @@ public function __construct($accessKey = null, $secretKey = null, $useSSL = fals
*/
public function setEndpoint($host)
{
self::$endpoint = $host;
// Extract port from endpoint if present
if (strpos($host, ':') !== false) {
$parts = explode(':', $host, 2);
self::$endpoint = $parts[0];
self::$endpointPort = ':' . $parts[1];
} else {
self::$endpoint = $host;
self::$endpointPort = '';
}
}

/**
* Set the storage type
*
* @param string $type Storage type: 'auto', 'aws', or 'minio'
* @return void
*/
public static function setStorageType($type)
{
self::$storageType = $type;
}


Expand Down Expand Up @@ -866,15 +916,33 @@ function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.c
$this->bucket = $bucket;
$this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/';

// Determine URL style based on storage_type setting
$useVirtualHostedStyle = false;
$storageType = isset(\S3Compatible::$storageType) ? \S3Compatible::$storageType : 'auto';
if ($storageType === 'aws') {
// Explicitly set to AWS - use virtual-hosted-style if bucket name is valid
$useVirtualHostedStyle = $this->dnsBucketName($this->bucket);
} elseif ($storageType === 'minio') {
// Explicitly set to MinIO - always use path-style
$useVirtualHostedStyle = false;
} else {
// Auto-detect: determine if this is an AWS endpoint
$isAWS = (strpos($endpoint, 'amazonaws.com') !== false || strpos($endpoint, 's3.') === 0);
$useVirtualHostedStyle = ($isAWS && $this->dnsBucketName($this->bucket));
}

if ($this->bucket !== '')
{
if ($this->dnsBucketName($this->bucket))
// Use virtual-hosted-style for AWS if enabled and bucket name is DNS-compatible
// Use path-style for MinIO or invalid bucket names
if ($useVirtualHostedStyle)
{
$this->headers['Host'] = $this->bucket.'.'.$this->endpoint;
$this->resource = '/'.$this->bucket.$this->uri;
}
else
{
// Path-style: bucket in URI path
$this->headers['Host'] = $this->endpoint;
if ($this->bucket !== '') $this->uri = '/'.$this->bucket.$this->uri;
$this->bucket = '';
Expand Down Expand Up @@ -959,7 +1027,14 @@ public function getResponse()
array_key_exists('logging', $this->parameters))
$this->resource .= $query;
}
$url = (S3Compatible::$useSSL ? 'https://' : 'http://') . ($this->headers['Host'] !== '' ? $this->headers['Host'] : $this->endpoint) . $this->uri;
// Build URL with port if specified
$host = ($this->headers['Host'] !== '' ? $this->headers['Host'] : $this->endpoint);
$port = isset(\S3Compatible::$endpointPort) ? \S3Compatible::$endpointPort : '';
// Don't add port to URL if host already contains port (for virtual-hosted-style)
if (strpos($host, ':') !== false) {
$port = '';
}
$url = (S3Compatible::$useSSL ? 'https://' : 'http://') . $host . $port . $this->uri;

// Basic setup
$curl = curl_init();
Expand Down