package handler import ( "context" "errors" "fmt" "net/http" "strconv" "strings" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/tree" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" frosterrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) func (h *handler) reqLogger(ctx context.Context) *zap.Logger { reqLogger := middleware.GetReqLog(ctx) if reqLogger != nil { return reqLogger } return h.log } func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo *middleware.ReqInfo, err error, additional ...zap.Field) { err = handleDeleteMarker(w, err) if code, wrErr := middleware.WriteErrorResponse(w, reqInfo, transformToS3Error(err)); wrErr != nil { additional = append(additional, zap.NamedError("write_response_error", wrErr)) } else { additional = append(additional, zap.Int("status", code)) } fields := []zap.Field{ zap.String("request_id", reqInfo.RequestID), zap.String("method", reqInfo.API), zap.String("bucket", reqInfo.BucketName), zap.String("object", reqInfo.ObjectName), zap.String("description", logText), zap.String("user", reqInfo.User), zap.Error(err)} fields = append(fields, additional...) if traceID, err := trace.TraceIDFromHex(reqInfo.TraceID); err == nil && traceID.IsValid() { fields = append(fields, zap.String("trace_id", reqInfo.TraceID)) } h.log.Error(logs.RequestFailed, fields...) // consider using h.reqLogger (it requires accept context.Context or http.Request) } func handleDeleteMarker(w http.ResponseWriter, err error) error { var target layer.DeleteMarkerError if !errors.As(err, &target) { return err } w.Header().Set(api.AmzDeleteMarker, "true") return fmt.Errorf("%w: %s", s3errors.GetAPIError(target.ErrorCode), err) } func transformToS3Error(err error) error { err = frosterrors.UnwrapErr(err) // this wouldn't work with errors.Join if _, ok := err.(s3errors.Error); ok { return err } if errors.Is(err, layer.ErrAccessDenied) || errors.Is(err, tree.ErrNodeAccessDenied) { return s3errors.GetAPIError(s3errors.ErrAccessDenied) } if errors.Is(err, layer.ErrGatewayTimeout) { return s3errors.GetAPIError(s3errors.ErrGatewayTimeout) } return s3errors.GetAPIError(s3errors.ErrInternalError) } func (h *handler) ResolveBucket(ctx context.Context, bucket string) (*data.BucketInfo, error) { return h.obj.GetBucketInfo(ctx, bucket) } func (h *handler) ResolveCID(ctx context.Context, bucket string) (cid.ID, error) { return h.obj.ResolveCID(ctx, bucket) } func (h *handler) getBucketAndCheckOwner(r *http.Request, bucket string, header ...string) (*data.BucketInfo, error) { bktInfo, err := h.obj.GetBucketInfo(r.Context(), bucket) if err != nil { return nil, err } var expected string if len(header) == 0 { expected = r.Header.Get(api.AmzExpectedBucketOwner) } else { expected = r.Header.Get(header[0]) } if len(expected) == 0 { return bktInfo, nil } return bktInfo, checkOwner(bktInfo, expected) } func (h *handler) getPutPayloadSize(r *http.Request) uint64 { decodeContentSize := r.Header.Get(api.AmzDecodedContentLength) decodedSize, err := strconv.Atoi(decodeContentSize) if err != nil { decodedSize = 0 } var size uint64 if decodedSize > 0 { size = uint64(decodedSize) } else if r.ContentLength > 0 { size = uint64(r.ContentLength) } return size } func parseRange(s string) (*layer.RangeParams, error) { if s == "" { return nil, nil } prefix := "bytes=" if !strings.HasPrefix(s, prefix) { return nil, s3errors.GetAPIError(s3errors.ErrInvalidRange) } s = strings.TrimPrefix(s, prefix) valuesStr := strings.Split(s, "-") if len(valuesStr) != 2 { return nil, s3errors.GetAPIError(s3errors.ErrInvalidRange) } values := make([]uint64, 0, len(valuesStr)) for _, v := range valuesStr { num, err := strconv.ParseUint(v, 10, 64) if err != nil { return nil, s3errors.GetAPIError(s3errors.ErrInvalidRange) } values = append(values, num) } if values[0] > values[1] { return nil, s3errors.GetAPIError(s3errors.ErrInvalidRange) } return &layer.RangeParams{ Start: values[0], End: values[1], }, nil }