package handler import ( "context" "errors" "fmt" "strings" "time" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client" sdkstatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/valyala/fasthttp" "go.uber.org/zap" ) type request struct { *fasthttp.RequestCtx log *zap.Logger } func (r *request) handleFrostFSErr(err error, start time.Time) { logFields := []zap.Field{ zap.Stringer("elapsed", time.Since(start)), zap.Error(err), } statusCode, msg, additionalFields := formErrorResponse("could not receive object", err) logFields = append(logFields, additionalFields...) r.log.Error(logs.CouldNotReceiveObject, logFields...) ResponseError(r.RequestCtx, msg, statusCode) } func bearerToken(ctx context.Context) *bearer.Token { if tkn, err := tokens.LoadBearerToken(ctx); err == nil { return tkn } return nil } func isDir(name string) bool { return name == "" || strings.HasSuffix(name, "/") } func loadAttributes(attrs []object.Attribute) map[string]string { result := make(map[string]string) for _, attr := range attrs { result[attr.Key()] = attr.Value() } return result } func isValidToken(s string) bool { for _, c := range s { if c <= ' ' || c > 127 { return false } if strings.ContainsRune("()<>@,;:\\\"/[]?={}", c) { return false } } return true } func isValidValue(s string) bool { for _, c := range s { // HTTP specification allows for more technically, but we don't want to escape things. if c < ' ' || c > 127 || c == '"' { return false } } return true } func logAndSendBucketError(c *fasthttp.RequestCtx, log *zap.Logger, err error) { log.Error(logs.CouldntGetBucket, zap.Error(err)) if client.IsErrContainerNotFound(err) { ResponseError(c, "Not Found", fasthttp.StatusNotFound) return } ResponseError(c, "could not get bucket: "+err.Error(), fasthttp.StatusBadRequest) } func newAddress(cnr cid.ID, obj oid.ID) oid.Address { var addr oid.Address addr.SetContainer(cnr) addr.SetObject(obj) return addr } func ResponseError(r *fasthttp.RequestCtx, msg string, code int) { r.Error(msg+"\n", code) } func formErrorResponse(message string, err error) (int, string, []zap.Field) { var ( msg string statusCode int logFields []zap.Field ) st := new(sdkstatus.ObjectAccessDenied) switch { case errors.As(err, &st): statusCode = fasthttp.StatusForbidden reason := st.Reason() msg = fmt.Sprintf("%s: %v: %s", message, err, reason) logFields = append(logFields, zap.String("error_detail", reason)) case errors.Is(err, ErrQuotaLimitReached): statusCode = fasthttp.StatusConflict msg = fmt.Sprintf("%s: %v", message, err) case client.IsErrObjectNotFound(err) || client.IsErrContainerNotFound(err): statusCode = fasthttp.StatusNotFound msg = "Not Found" default: statusCode = fasthttp.StatusBadRequest msg = fmt.Sprintf("%s: %v", message, err) } return statusCode, msg, logFields }