Files
s3-balance/internal/api/bucket_handler.go
2025-10-04 15:18:45 +08:00

274 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package api
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
)
// handleListBuckets 处理列出所有存储桶请求
func (h *S3Handler) handleListBuckets(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {
if h.storage == nil {
return
}
h.recordAccessLog(r, "list_buckets", "", "", 0, true, "", time.Since(start))
}()
buckets := h.bucketManager.GetAllBuckets()
result := ListBucketsResult{
Xmlns: "http://s3.amazonaws.com/doc/2006-03-01/",
Owner: Owner{
ID: "s3-balance",
DisplayName: "S3 Balance Service",
},
Buckets: Buckets{
Bucket: make([]BucketInfo, 0, len(buckets)),
},
}
for _, b := range buckets {
// 只显示启用的虚拟存储桶,对客户端隐藏底层真实存储桶
if b.IsAvailable() && b.Config.Enabled && b.Config.Virtual {
result.Buckets.Bucket = append(result.Buckets.Bucket, BucketInfo{
Name: b.Config.Name,
CreationDate: time.Now().Add(-24 * time.Hour), // 模拟创建时间
})
}
}
h.sendXMLResponse(w, http.StatusOK, result)
}
// handleBucketOperations 处理存储桶相关操作
func (h *S3Handler) handleBucketOperations(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bucketName := vars["bucket"]
// 记录操作指标
start := time.Now()
method := r.Method
var status = "success"
defer func() {
if h.metrics != nil {
duration := time.Since(start).Seconds()
h.metrics.RecordS3Operation(method, bucketName, status)
h.metrics.RecordS3OperationDuration(method, bucketName, duration)
}
}()
switch r.Method {
case "GET":
h.handleListObjects(w, r, bucketName)
case "HEAD":
h.handleHeadBucket(w, r, bucketName)
case "PUT":
h.handleCreateBucket(w, r, bucketName)
case "DELETE":
h.handleDeleteBucket(w, r, bucketName)
}
}
// handleListObjects 列出存储桶中的对象
func (h *S3Handler) handleListObjects(w http.ResponseWriter, r *http.Request, bucketName string) {
// 检查bucket是否存在
bucket, ok := h.bucketManager.GetBucket(bucketName)
if !ok {
h.sendS3Error(w, "NoSuchBucket", "The specified bucket does not exist", bucketName)
return
}
// 如果是虚拟存储桶,列出虚拟存储桶中的对象
if bucket.IsVirtual() {
h.handleListObjectsForVirtualBucket(w, r, bucketName)
return
}
// 如果不是虚拟存储桶,拒绝客户端访问真实存储桶
h.sendS3Error(w, "NoSuchBucket", "The specified bucket does not exist", bucketName)
}
// handleListObjectsForVirtualBucket 列出虚拟存储桶中的对象
func (h *S3Handler) handleListObjectsForVirtualBucket(w http.ResponseWriter, r *http.Request, bucketName string) {
// 解析查询参数
prefix := r.URL.Query().Get("prefix")
marker := r.URL.Query().Get("marker")
maxKeysStr := r.URL.Query().Get("max-keys")
delimiter := r.URL.Query().Get("delimiter")
maxKeys := 1000
if maxKeysStr != "" {
if mk, err := strconv.Atoi(maxKeysStr); err == nil {
maxKeys = mk
}
}
// 从存储服务获取虚拟存储桶中的对象
objects, err := h.storage.GetVirtualBucketObjects(bucketName)
if err != nil {
h.sendS3Error(w, "InternalError", "Failed to list virtual bucket objects", bucketName)
return
}
result := ListBucketResult{
Xmlns: "http://s3.amazonaws.com/doc/2006-03-01/",
Name: bucketName,
Prefix: prefix,
Marker: marker,
MaxKeys: maxKeys,
Delimiter: delimiter,
IsTruncated: false,
Contents: make([]ObjectInfo, 0, len(objects)),
CommonPrefixes: make([]CommonPrefix, 0),
}
// 用于跟踪已添加的公共前缀
prefixSet := make(map[string]bool)
// 过滤对象并转换为S3格式
for _, obj := range objects {
// 前缀过滤
if prefix != "" && !strings.HasPrefix(obj.Key, prefix) {
continue
}
// Marker过滤
if marker != "" && obj.Key <= marker {
continue
}
// 如果设置了delimiter处理目录结构
if delimiter != "" {
// 移除prefix部分然后查找delimiter
keyAfterPrefix := obj.Key
if prefix != "" {
keyAfterPrefix = strings.TrimPrefix(obj.Key, prefix)
}
// 查找delimiter的位置
delimiterPos := strings.Index(keyAfterPrefix, delimiter)
if delimiterPos >= 0 {
// 这是一个"目录"
commonPrefix := prefix + keyAfterPrefix[:delimiterPos+1]
if !prefixSet[commonPrefix] {
result.CommonPrefixes = append(result.CommonPrefixes, CommonPrefix{
Prefix: commonPrefix,
})
prefixSet[commonPrefix] = true
}
continue
}
}
// 添加到结果中
result.Contents = append(result.Contents, ObjectInfo{
Key: obj.Key,
LastModified: obj.UpdatedAt,
ETag: fmt.Sprintf("\"%x\"", obj.ID),
Size: obj.Size,
})
}
// 如果超过了最大数量,设置截断标志
totalItems := len(result.Contents) + len(result.CommonPrefixes)
if totalItems > maxKeys {
// 简化处理如果总数超过maxKeys截断Contents
if len(result.Contents) > maxKeys {
result.Contents = result.Contents[:maxKeys]
result.IsTruncated = true
result.CommonPrefixes = nil
}
}
h.sendXMLResponse(w, http.StatusOK, result)
}
// handleHeadBucket 检查存储桶是否存在
func (h *S3Handler) handleHeadBucket(w http.ResponseWriter, r *http.Request, bucketName string) {
bucket, ok := h.bucketManager.GetBucket(bucketName)
if !ok {
w.WriteHeader(http.StatusNotFound)
return
}
// 虚拟存储桶也应该返回成功状态
if bucket.IsVirtual() {
w.WriteHeader(http.StatusOK)
return
}
// 如果不是虚拟存储桶,拒绝客户端访问真实存储桶
w.WriteHeader(http.StatusNotFound)
}
// handleCreateBucket 创建存储桶(虚拟实现)
func (h *S3Handler) handleCreateBucket(w http.ResponseWriter, r *http.Request, bucketName string) {
// 检查是否已经存在同名存储桶
if bucket, exists := h.bucketManager.GetBucket(bucketName); exists {
// 如果是虚拟存储桶,检查是否已经有映射
if bucket.IsVirtual() {
// 虚拟存储桶不需要检查映射,文件级映射只在有文件时才创建
h.sendS3Error(w, "BucketAlreadyExists", "The requested bucket name is not available", bucketName)
return
} else {
// 如果是真实存储桶,返回已存在错误
h.sendS3Error(w, "BucketAlreadyExists", "The requested bucket name is not available", bucketName)
return
}
}
// 检查是否为虚拟存储桶
if requestedBucket, exists := h.bucketManager.GetBucket(bucketName); exists && requestedBucket.IsVirtual() {
// 虚拟存储桶需要选择一个真实存储桶进行映射
realBuckets := h.bucketManager.GetRealBuckets()
if len(realBuckets) == 0 {
h.sendS3Error(w, "InternalError", "No real buckets available for virtual bucket mapping", bucketName)
return
}
// 简化:选择第一个可用的真实存储桶
// 实际应用中可能需要更复杂的策略
targetBucket := realBuckets[0]
// 创建虚拟存储桶到真实存储桶的映射
if err := h.storage.CreateVirtualBucketMapping(bucketName, "", targetBucket.Config.Name); err != nil {
h.sendS3Error(w, "InternalError", "Failed to create virtual bucket mapping", bucketName)
return
}
}
// 在负载均衡场景下不真正创建bucket只返回成功
// 实际的bucket应该在配置中预先定义
w.Header().Set("Location", "/"+bucketName)
w.WriteHeader(http.StatusOK)
}
// handleDeleteBucket 删除存储桶(虚拟实现)
func (h *S3Handler) handleDeleteBucket(w http.ResponseWriter, r *http.Request, bucketName string) {
// 检查存储桶是否存在
bucket, exists := h.bucketManager.GetBucket(bucketName)
if !exists {
// 不存在的桶返回成功S3标准
w.WriteHeader(http.StatusNoContent)
return
}
// 虚拟存储桶需要删除映射关系
if bucket.IsVirtual() {
// 删除虚拟存储桶映射
if err := h.storage.DeleteVirtualBucketMapping(bucketName); err != nil {
h.sendS3Error(w, "InternalError", "Failed to delete virtual bucket mapping", bucketName)
return
}
}
// 在负载均衡场景下不真正删除真实bucket
w.WriteHeader(http.StatusNoContent)
}