github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/s3/err.go (about)

     1  // Package s3 provides Amazon S3 compatibility layer
     2  /*
     3   * Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package s3
     6  
     7  import (
     8  	"encoding/xml"
     9  	"fmt"
    10  	"net/http"
    11  	"strings"
    12  
    13  	"github.com/NVIDIA/aistore/api/apc"
    14  	"github.com/NVIDIA/aistore/cmn"
    15  	"github.com/NVIDIA/aistore/cmn/cos"
    16  	"github.com/NVIDIA/aistore/cmn/debug"
    17  	"github.com/NVIDIA/aistore/core"
    18  	"github.com/NVIDIA/aistore/memsys"
    19  )
    20  
    21  const ErrPrefix = "aws-error"
    22  
    23  type Error struct {
    24  	Code      string
    25  	Message   string
    26  	Resource  string
    27  	RequestID string `xml:"RequestId"`
    28  }
    29  
    30  func (e *Error) mustMarshal(sgl *memsys.SGL) {
    31  	sgl.Write([]byte(xml.Header))
    32  	err := xml.NewEncoder(sgl).Encode(e)
    33  	debug.AssertNoErr(err)
    34  }
    35  
    36  // with user-friendly tip
    37  func WriteMptErr(w http.ResponseWriter, r *http.Request, err error, ecode int, lom *core.LOM, uploadID string) {
    38  	// specifically, for s3cmd example
    39  	name := strings.Replace(lom.Cname(), apc.AISScheme+apc.BckProviderSeparator, apc.S3Scheme+apc.BckProviderSeparator, 1)
    40  	s3cmd := "s3cmd abortmp " + name + " " + uploadID
    41  	if len(s3cmd) > 50 {
    42  		s3cmd = "\n  " + s3cmd
    43  	}
    44  	e := fmt.Errorf("%v\nUse upload ID %q to cleanup, e.g.: %s", err, uploadID, s3cmd)
    45  	if ecode == 0 {
    46  		ecode = http.StatusInternalServerError
    47  	}
    48  	WriteErr(w, r, e, ecode)
    49  }
    50  
    51  func WriteErr(w http.ResponseWriter, r *http.Request, err error, ecode int) {
    52  	var (
    53  		out       Error
    54  		in        *cmn.ErrHTTP
    55  		ok        bool
    56  		allocated bool
    57  	)
    58  	if in, ok = err.(*cmn.ErrHTTP); !ok {
    59  		in = cmn.InitErrHTTP(r, err, ecode)
    60  		allocated = true
    61  	}
    62  	out.Message = in.Message
    63  	switch {
    64  	case cmn.IsErrBucketAlreadyExists(err):
    65  		out.Code = "BucketAlreadyExists"
    66  	case cmn.IsErrBckNotFound(err):
    67  		out.Code = "NoSuchBucket"
    68  	case in.TypeCode != "":
    69  		out.Code = in.TypeCode
    70  	default:
    71  		l := len(ErrPrefix)
    72  		// e.g. "aws-error[NotFound: blah]" as per backend/aws.go _awsErr() formatting
    73  		if strings.HasPrefix(out.Message, ErrPrefix) {
    74  			if i := strings.Index(out.Message[l+1:], ":"); i > 4 {
    75  				code := out.Message[l+1 : l+i+1]
    76  				if cos.IsAlphaNice(code) && code[0] >= 'A' && code[0] <= 'Z' {
    77  					out.Code = code
    78  				}
    79  			}
    80  		}
    81  	}
    82  	sgl := memsys.PageMM().NewSGL(0)
    83  	out.mustMarshal(sgl)
    84  
    85  	w.Header().Set(cos.HdrContentType, cos.ContentXML)
    86  	w.Header().Set(cos.HdrContentTypeOptions, "nosniff")
    87  
    88  	w.WriteHeader(in.Status)
    89  	sgl.WriteTo2(w)
    90  	sgl.Free()
    91  	if allocated {
    92  		cmn.FreeHterr(in)
    93  	}
    94  }