feat: add UseVirtualHostedStyle flag for S3-compatible services

S3-compatible services such as Alibaba Cloud OSS, Tencent COS and
Huawei OBS reject path-style URLs with 'SecondLevelDomainForbidden'
and only accept virtual-hosted style (https://bucket.endpoint/key).

This adds a UseVirtualHostedStyle field and SetVirtualHostedStyle()
setter. When enabled and a custom Endpoint is set, the first path
segment is treated as the bucket and prepended to the host as a
subdomain. AWS S3 and MinIO accept both styles, so this is a no-op
for them.

Includes tests for OSS-style endpoint, http/https scheme preservation
and the empty-path fallback used by ListBuckets-style operations.
This commit is contained in:
ys-ll
2026-08-01 23:42:15 +08:00
parent 1d8c6e314b
commit 9210db974c
3 changed files with 145 additions and 2 deletions
+23 -2
View File
@@ -29,8 +29,17 @@ type S3 struct {
Token string
Endpoint string
URIFormat string
initMode string
expiry time.Time
// UseVirtualHostedStyle enables virtual-hosted style addressing when a
// custom Endpoint is set. In virtual-hosted style the bucket name is
// embedded as a subdomain of the endpoint
// (e.g. https://mybucket.oss-cn-hangzhou.aliyuncs.com/key) instead of
// appearing in the URL path (https://oss-cn-hangzhou.aliyuncs.com/mybucket/key).
// S3-compatible services such as Alibaba Cloud OSS, Tencent COS and
// some private clouds reject path-style requests with
// "SecondLevelDomainForbidden", so this flag is required for them.
UseVirtualHostedStyle bool
initMode string
expiry time.Time
mu sync.Mutex
}
@@ -92,6 +101,18 @@ func (s3 *S3) SetClient(client *http.Client) *S3 {
return s3
}
// SetVirtualHostedStyle toggles virtual-hosted style URL addressing for S3
// operations against a custom Endpoint. When enabled, the bucket name is
// embedded as a subdomain of the endpoint
// (https://mybucket.<endpoint>/<key>). S3-compatible services that reject
// path-style URLs (Alibaba Cloud OSS, Tencent COS, Huawei OBS, etc.) need
// this enabled. AWS S3 supports both styles; this is a no-op when no custom
// Endpoint is set.
func (s3 *S3) SetVirtualHostedStyle(enabled bool) *S3 {
s3.UseVirtualHostedStyle = enabled
return s3
}
func (s3 *S3) signRequest(req *http.Request) error {
var (
err error