目录

目录

Minio使用S3协议时自定义Virtual Hosted Style

目录

Minio使用S3协议时自定义Virtual Hosted Style

遇到一个minio使用COS作为s3存储时,多出来一个桶名路径的问题。本来以为是配置项的问题,各种方式填了个遍还是不行。最后狠下新来读源码,发现minio在构造请求url时有virtual host stylepath style两种方式:

    urlStr := scheme + "://" + host + "/"

    // Make URL only if bucketName is available, otherwise use the
    // endpoint URL.
    if bucketName != "" {
        // If endpoint supports virtual host style use that always.
        // Currently only S3 and Google Cloud Storage would support
        // virtual host style.
        if isVirtualHostStyle {
            urlStr = scheme + "://" + bucketName + "." + host + "/"
            if objectName != "" {
                urlStr += s3utils.EncodePath(objectName)
            }
        } else {
            // If not fall back to using path style.
            urlStr = urlStr + bucketName + "/"
            if objectName != "" {
                urlStr += s3utils.EncodePath(objectName)
            }
        }
    }

其中前者是bucketName.host的形式,即COS的形式,后者是host/bucketName。COS没有被识别成前者,就按照后者构造成了 bucketName.host/bucketName,额外的bucketName就被当作了文件路径。

找到了原因就该尝试着手修复,isVirtualHostStyle的判断如下:

// returns true if virtual hosted style requests are to be used.
func (c *Client) isVirtualHostStyleRequest(url url.URL, bucketName string) bool {
    if c.lookupFn != nil {
        lookup := c.lookupFn(url, bucketName)
        switch lookup {
        case BucketLookupDNS:
            return true
        case BucketLookupPath:
            return false
        }
        // if its auto then we fallback to default detection.
        return s3utils.IsVirtualHostSupported(url, bucketName)
    }

    if bucketName == "" {
        return false
    }

    if c.lookup == BucketLookupDNS {
        return true
    }

    if c.lookup == BucketLookupPath {
        return false
    }

    // default to virtual only for Amazon/Google storage. In all other cases use
    // path style requests
    return s3utils.IsVirtualHostSupported(url, bucketName)
}

大致捋一下逻辑,首先通过lookupFn这个自定义的判断函数去判断类型,然后看是否存在预设值lookup,最后使用minio内置的IsVirtualHostSupported函数去判断类型。具体实现就是判断url中有没有AWS/Google/阿里云OSS的域名,在此不作展开。

可以看到,minio已经预留了lookupFn函数和lookup值用于二次开发。而我们只需要实现lookupFn函数,在创建client时传入就行:

    client, err := minio.New(endpoint, &minio.Options{
        Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
        Secure: secure,
        Region: region,
        BucketLookupViaURL: CustomBucketLookupViaURL,
    })


// IsTencentCOSEndpoint - Match if it is exactly Tencent Cloud COS endpoint.
func IsTencentCOSEndpoint(endpointURL url.URL) bool {
    hostname := endpointURL.Hostname()

    return strings.HasSuffix(hostname, "myqcloud.com") ||
        strings.HasSuffix(hostname, "tencentcos.cn")
}

// CustomBucketLookupViaURL implements minio.BucketLookupViaURL function.
func CustomBucketLookupViaURL(endpointURL url.URL, bucketName string) minio.BucketLookupType {
    // 腾讯云COS支持VirtualHostStyle
    if IsTencentCOSEndpoint(endpointURL) {
        return minio.BucketLookupDNS
    }
    return minio.BucketLookupAuto
}

最后,在吭哧吭哧一顿读代码写代码打包镜像测试后,终于是修复了这个问题并提交了pr。然后我去minio项目下看了下能不能也提个pr,结果发现早有人这么干了:feat: using virtual hosted style for Tencent Cloud COS。但维护者明确指出不再专门添加后端,而是由用户自行设置,所以并没有被合并。

经典重复造轮子,要是早点搜一下就好了。