[#233] get/head: Middleware refactor
Add: * search index.html * fallback by leading slash Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
ee628617a3
commit
9cb9d14146
11 changed files with 311 additions and 121 deletions
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
|
||||
|
@ -31,13 +32,18 @@ func (h *Handler) DownloadByAddressOrBucketName(req *fasthttp.RequestCtx) {
|
|||
|
||||
cidParam := req.UserValue("cid").(string)
|
||||
oidParam := req.UserValue("oid").(string)
|
||||
downloadParam := req.QueryArgs().GetBool("download")
|
||||
|
||||
ctx = utils.SetReqLog(ctx, h.reqLogger(ctx).With(
|
||||
zap.String("cid", cidParam),
|
||||
zap.String("oid", oidParam),
|
||||
))
|
||||
|
||||
path, err := url.QueryUnescape(oidParam)
|
||||
if err != nil {
|
||||
h.logAndSendError(ctx, req, logs.FailedToUnescapePath, err)
|
||||
return
|
||||
}
|
||||
|
||||
bktInfo, err := h.getBucketInfo(ctx, cidParam)
|
||||
if err != nil {
|
||||
h.logAndSendError(ctx, req, logs.FailedToGetBucketInfo, err)
|
||||
|
@ -50,18 +56,159 @@ func (h *Handler) DownloadByAddressOrBucketName(req *fasthttp.RequestCtx) {
|
|||
return
|
||||
}
|
||||
|
||||
var objID oid.ID
|
||||
if checkS3Err == nil && shouldDownload(oidParam, downloadParam) {
|
||||
h.byS3Path(ctx, req, bktInfo.CID, oidParam, h.receiveFile)
|
||||
} else if err = objID.DecodeString(oidParam); err == nil {
|
||||
h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.receiveFile)
|
||||
prm := MiddlewareParam{
|
||||
Context: ctx,
|
||||
Request: req,
|
||||
BktInfo: bktInfo,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
indexPageEnabled := h.config.IndexPageEnabled()
|
||||
|
||||
if checkS3Err == nil {
|
||||
run(prm, h.errorMiddleware(logs.ObjectNotFound, ErrObjectNotFound),
|
||||
Middleware{Func: h.byS3PathMiddleware(h.receiveFile, noopFormer), Enabled: true},
|
||||
Middleware{Func: h.byS3PathMiddleware(h.receiveFile, indexFormer), Enabled: indexPageEnabled},
|
||||
Middleware{Func: h.browseIndexMiddleware(h.getDirObjectsS3), Enabled: indexPageEnabled},
|
||||
)
|
||||
} else {
|
||||
h.browseIndex(ctx, req, cidParam, oidParam, checkS3Err != nil)
|
||||
slashFallbackEnabled := h.config.EnableFilepathSlashFallback()
|
||||
fileNameFallbackEnabled := h.config.EnableFilepathFallback()
|
||||
|
||||
run(prm, h.errorMiddleware(logs.ObjectNotFound, ErrObjectNotFound),
|
||||
Middleware{Func: h.byAddressMiddleware(h.receiveFile), Enabled: true},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFilePath, noopFormer), Enabled: true},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFilePath, reverseLeadingSlash), Enabled: slashFallbackEnabled},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFileName, noopFormer), Enabled: fileNameFallbackEnabled},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFileName, reverseLeadingSlash), Enabled: fileNameFallbackEnabled && slashFallbackEnabled},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFilePath, indexFormer), Enabled: indexPageEnabled},
|
||||
Middleware{Func: h.byAttributeSearchMiddleware(h.receiveFile, object.AttributeFileName, indexFormer), Enabled: fileNameFallbackEnabled && indexPageEnabled},
|
||||
Middleware{Func: h.browseIndexMiddleware(h.getDirObjectsNative), Enabled: indexPageEnabled},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func shouldDownload(oidParam string, downloadParam bool) bool {
|
||||
return !isDir(oidParam) || downloadParam
|
||||
type MiddlewareFunc func(param MiddlewareParam) bool
|
||||
|
||||
type MiddlewareParam struct {
|
||||
Context context.Context
|
||||
Request *fasthttp.RequestCtx
|
||||
BktInfo *data.BucketInfo
|
||||
Path string
|
||||
}
|
||||
|
||||
type Middleware struct {
|
||||
Func MiddlewareFunc
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func run(prm MiddlewareParam, defaultMiddleware MiddlewareFunc, middlewares ...Middleware) {
|
||||
for _, m := range middlewares {
|
||||
if m.Enabled && !m.Func(prm) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
defaultMiddleware(prm)
|
||||
}
|
||||
|
||||
func indexFormer(path string) string {
|
||||
indexPath := path
|
||||
if indexPath != "" && !strings.HasSuffix(indexPath, "/") {
|
||||
indexPath += "/"
|
||||
}
|
||||
|
||||
return indexPath + "index.html"
|
||||
}
|
||||
|
||||
func reverseLeadingSlash(path string) string {
|
||||
if path == "" || path == "/" {
|
||||
return path
|
||||
}
|
||||
|
||||
if path[0] == '/' {
|
||||
return path[1:]
|
||||
}
|
||||
|
||||
return "/" + path
|
||||
}
|
||||
|
||||
func noopFormer(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func (h *Handler) byS3PathMiddleware(handler func(context.Context, *fasthttp.RequestCtx, oid.Address), pathFormer func(string) string) MiddlewareFunc {
|
||||
return func(prm MiddlewareParam) bool {
|
||||
ctx, span := tracing.StartSpanFromContext(prm.Context, "handler.byS3Path")
|
||||
defer span.End()
|
||||
|
||||
path := pathFormer(prm.Path)
|
||||
|
||||
foundOID, err := h.tree.GetLatestVersion(ctx, &prm.BktInfo.CID, path)
|
||||
if err == nil {
|
||||
if foundOID.IsDeleteMarker {
|
||||
h.logAndSendError(ctx, prm.Request, logs.IndexWasDeleted, ErrObjectNotFound)
|
||||
return false
|
||||
}
|
||||
|
||||
addr := newAddress(prm.BktInfo.CID, foundOID.OID)
|
||||
handler(ctx, prm.Request, addr)
|
||||
return false
|
||||
}
|
||||
|
||||
if !errors.Is(err, layer.ErrNodeNotFound) {
|
||||
h.logAndSendError(ctx, prm.Request, logs.FailedToGetLatestVersionOfIndexObject, err, zap.String("path", path))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) byAttributeSearchMiddleware(handler func(context.Context, *fasthttp.RequestCtx, oid.Address), attr string, pathFormer func(string) string) MiddlewareFunc {
|
||||
return func(prm MiddlewareParam) bool {
|
||||
ctx, span := tracing.StartSpanFromContext(prm.Context, "handler.byAttributeSearch")
|
||||
defer span.End()
|
||||
|
||||
path := pathFormer(prm.Path)
|
||||
|
||||
res, err := h.search(ctx, prm.BktInfo.CID, attr, path, object.MatchStringEqual)
|
||||
if err != nil {
|
||||
h.logAndSendError(ctx, prm.Request, logs.FailedToFindObjectByAttribute, err)
|
||||
return false
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
buf := make([]oid.ID, 1)
|
||||
n, err := res.Read(buf)
|
||||
if err == nil && n > 0 {
|
||||
addr := newAddress(prm.BktInfo.CID, buf[0])
|
||||
handler(ctx, prm.Request, addr)
|
||||
return false
|
||||
}
|
||||
|
||||
if !errors.Is(err, io.EOF) {
|
||||
h.logAndSendError(ctx, prm.Request, logs.FailedToFindObjectByAttribute, err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) byAddressMiddleware(handler func(context.Context, *fasthttp.RequestCtx, oid.Address)) MiddlewareFunc {
|
||||
return func(prm MiddlewareParam) bool {
|
||||
ctx, span := tracing.StartSpanFromContext(prm.Context, "handler.byAddress")
|
||||
defer span.End()
|
||||
|
||||
var objID oid.ID
|
||||
if objID.DecodeString(prm.Path) == nil {
|
||||
handler(ctx, prm.Request, newAddress(prm.BktInfo.CID, objID))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadByAttribute handles attribute-based download requests.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue