forked from TrueCloudLab/frostfs-s3-gw
134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
|
package middleware
|
||
|
|
||
|
import (
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
const wildcardPlaceholder = "<wildcard>"
|
||
|
|
||
|
type VHSSettings interface {
|
||
|
Domains() []string
|
||
|
GlobalVHS() bool
|
||
|
VHSNamespacesEnabled() map[string]bool
|
||
|
}
|
||
|
|
||
|
func PrepareAddressStyle(settings VHSSettings, log *zap.Logger) Func {
|
||
|
return func(h http.Handler) http.Handler {
|
||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
ctx := r.Context()
|
||
|
reqInfo := GetReqInfo(ctx)
|
||
|
reqLogger := reqLogOrDefault(ctx, log)
|
||
|
|
||
|
if isVHSAddress(settings.GlobalVHS(), settings.VHSNamespacesEnabled(), reqInfo.Namespace) {
|
||
|
prepareVHSAddress(reqInfo, r, settings)
|
||
|
} else {
|
||
|
preparePathStyleAddress(reqInfo, r, reqLogger)
|
||
|
}
|
||
|
|
||
|
h.ServeHTTP(w, r)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isVHSAddress(enabledFlag bool, vhsNamespaces map[string]bool, namespace string) bool {
|
||
|
result := enabledFlag
|
||
|
|
||
|
if v, ok := vhsNamespaces[namespace]; ok {
|
||
|
result = v
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func prepareVHSAddress(reqInfo *ReqInfo, r *http.Request, settings VHSSettings) {
|
||
|
reqInfo.RequestVHSEnabled = true
|
||
|
bktName, match := checkDomain(r.Host, settings.Domains())
|
||
|
if match {
|
||
|
if bktName == "" {
|
||
|
reqInfo.RequestType = noneType
|
||
|
} else {
|
||
|
if objName := strings.TrimPrefix(r.URL.Path, "/"); objName != "" {
|
||
|
reqInfo.RequestType = objectType
|
||
|
reqInfo.ObjectName = objName
|
||
|
reqInfo.BucketName = bktName
|
||
|
} else {
|
||
|
reqInfo.RequestType = bucketType
|
||
|
reqInfo.BucketName = bktName
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
parts := strings.Split(r.Host, ".")
|
||
|
reqInfo.BucketName = parts[0]
|
||
|
|
||
|
if objName := strings.TrimPrefix(r.URL.Path, "/"); objName != "" {
|
||
|
reqInfo.RequestType = objectType
|
||
|
reqInfo.ObjectName = objName
|
||
|
} else {
|
||
|
reqInfo.RequestType = bucketType
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func preparePathStyleAddress(reqInfo *ReqInfo, r *http.Request, reqLogger *zap.Logger) {
|
||
|
bktObj := strings.TrimPrefix(r.URL.Path, "/")
|
||
|
if bktObj == "" {
|
||
|
reqInfo.RequestType = noneType
|
||
|
} else if ind := strings.IndexByte(bktObj, '/'); ind != -1 && bktObj[ind+1:] != "" {
|
||
|
reqInfo.RequestType = objectType
|
||
|
reqInfo.BucketName = bktObj[:ind]
|
||
|
reqInfo.ObjectName = bktObj[ind+1:]
|
||
|
|
||
|
if r.URL.RawPath != "" {
|
||
|
// we have to do this because of
|
||
|
// https://github.com/go-chi/chi/issues/641
|
||
|
// https://github.com/go-chi/chi/issues/642
|
||
|
if obj, err := url.PathUnescape(reqInfo.ObjectName); err != nil {
|
||
|
reqLogger.Warn(logs.FailedToUnescapeObjectName, zap.Error(err))
|
||
|
} else {
|
||
|
reqInfo.ObjectName = obj
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
reqInfo.RequestType = bucketType
|
||
|
reqInfo.BucketName = strings.TrimSuffix(bktObj, "/")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func checkDomain(host string, domains []string) (bktName string, match bool) {
|
||
|
partsHost := strings.Split(host, ".")
|
||
|
for _, pattern := range domains {
|
||
|
partsPattern := strings.Split(pattern, ".")
|
||
|
bktName, match = compareMatch(partsHost, partsPattern)
|
||
|
if match {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func compareMatch(host, pattern []string) (bktName string, match bool) {
|
||
|
if len(host) < len(pattern) {
|
||
|
return "", false
|
||
|
}
|
||
|
|
||
|
i, j := len(host)-1, len(pattern)-1
|
||
|
for j >= 0 && (pattern[j] == wildcardPlaceholder || host[i] == pattern[j]) {
|
||
|
i--
|
||
|
j--
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case i == -1:
|
||
|
return "", true
|
||
|
case i == 0 && (j != 0 || host[i] == pattern[j]):
|
||
|
return host[0], true
|
||
|
default:
|
||
|
return "", false
|
||
|
}
|
||
|
}
|