forked from TrueCloudLab/frostfs-s3-gw
Merge pull request #185 from KirillovDenis/feature/184-unify_error_handling
[#184] Unify error handling
This commit is contained in:
commit
57f761d01a
17 changed files with 196 additions and 541 deletions
|
@ -1,7 +1,6 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -118,6 +117,7 @@ const (
|
||||||
ErrObjectLockInvalidHeaders
|
ErrObjectLockInvalidHeaders
|
||||||
ErrInvalidTagDirective
|
ErrInvalidTagDirective
|
||||||
// Add new error codes here.
|
// Add new error codes here.
|
||||||
|
ErrNotSupported
|
||||||
|
|
||||||
// SSE-S3 related API errors.
|
// SSE-S3 related API errors.
|
||||||
ErrInvalidEncryptionMethod
|
ErrInvalidEncryptionMethod
|
||||||
|
@ -967,6 +967,12 @@ var errorCodes = errorCodeMap{
|
||||||
Description: "Unknown tag directive.",
|
Description: "Unknown tag directive.",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
ErrNotSupported: {
|
||||||
|
ErrCode: ErrNotSupported,
|
||||||
|
Code: "BadRequest",
|
||||||
|
Description: "Not supported by NeoFS S3 Gateway",
|
||||||
|
HTTPStatusCode: http.StatusNotImplemented,
|
||||||
|
},
|
||||||
ErrInvalidEncryptionMethod: {
|
ErrInvalidEncryptionMethod: {
|
||||||
ErrCode: ErrInvalidEncryptionMethod,
|
ErrCode: ErrInvalidEncryptionMethod,
|
||||||
Code: "InvalidRequest",
|
Code: "InvalidRequest",
|
||||||
|
@ -1898,15 +1904,10 @@ func GetAPIError(code ErrorCode) Error {
|
||||||
|
|
||||||
// getErrorResponse gets in standard error and resource value and
|
// getErrorResponse gets in standard error and resource value and
|
||||||
// provides a encodable populated response values.
|
// provides a encodable populated response values.
|
||||||
func getAPIErrorResponse(ctx context.Context, err error, resource, requestID, hostID string) ErrorResponse {
|
func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse {
|
||||||
code := "InternalError"
|
code := "InternalError"
|
||||||
desc := err.Error()
|
desc := err.Error()
|
||||||
|
|
||||||
info := GetReqInfo(ctx)
|
|
||||||
if info == nil {
|
|
||||||
info = &ReqInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e, ok := err.(Error); ok {
|
if e, ok := err.(Error); ok {
|
||||||
code = e.Code
|
code = e.Code
|
||||||
desc = e.Description
|
desc = e.Description
|
||||||
|
@ -1917,9 +1918,9 @@ func getAPIErrorResponse(ctx context.Context, err error, resource, requestID, ho
|
||||||
Message: desc,
|
Message: desc,
|
||||||
BucketName: info.BucketName,
|
BucketName: info.BucketName,
|
||||||
Key: info.ObjectName,
|
Key: info.ObjectName,
|
||||||
Resource: resource,
|
Resource: info.URL.Path,
|
||||||
RequestID: requestID,
|
RequestID: info.RequestID,
|
||||||
HostID: hostID,
|
HostID: info.DeploymentID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,6 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const notSupported = "Not supported by NeoFS S3 Gate: "
|
|
||||||
|
|
||||||
var _ api.Handler = (*handler)(nil)
|
var _ api.Handler = (*handler)(nil)
|
||||||
|
|
||||||
// New creates new api.Handler using given logger and client.
|
// New creates new api.Handler using given logger and client.
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -31,10 +30,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
err error
|
err error
|
||||||
inf *layer.ObjectInfo
|
inf *layer.ObjectInfo
|
||||||
|
|
||||||
req = mux.Vars(r)
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
bkt = req["bucket"]
|
|
||||||
obj = req["object"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
src := r.Header.Get("X-Amz-Copy-Source")
|
src := r.Header.Get("X-Amz-Copy-Source")
|
||||||
|
@ -48,12 +44,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// its non "null" value, we should error out since we do not support
|
// its non "null" value, we should error out since we do not support
|
||||||
// any versions other than "null".
|
// any versions other than "null".
|
||||||
if vid := u.Query().Get("versionId"); vid != "" && vid != "null" {
|
if vid := u.Query().Get("versionId"); vid != "" && vid != "null" {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "no such version", reqInfo, api.GetAPIError(api.ErrNoSuchVersion))
|
||||||
Code: api.GetAPIError(api.ErrNoSuchVersion).Code,
|
|
||||||
Description: "",
|
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,34 +55,34 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
args, err := parseCopyObjectArgs(r.Header)
|
args, err := parseCopyObjectArgs(r.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, r, h.log, "could not parse request params ", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not parse request params", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if inf, err = h.obj.GetObjectInfo(r.Context(), srcBucket, srcObject); err != nil {
|
if inf, err = h.obj.GetObjectInfo(r.Context(), srcBucket, srcObject); err != nil {
|
||||||
writeError(w, r, h.log, "could not find object", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not find object", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = checkPreconditions(inf, args.Conditional); err != nil {
|
if err = checkPreconditions(inf, args.Conditional); err != nil {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrPreconditionFailed), r.URL)
|
h.logAndSendError(w, "precondition failed", reqInfo, api.GetAPIError(api.ErrPreconditionFailed))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
params := &layer.CopyObjectParams{
|
params := &layer.CopyObjectParams{
|
||||||
SrcBucket: srcBucket,
|
SrcBucket: srcBucket,
|
||||||
DstBucket: bkt,
|
DstBucket: reqInfo.BucketName,
|
||||||
SrcObject: srcObject,
|
SrcObject: srcObject,
|
||||||
DstObject: obj,
|
DstObject: reqInfo.ObjectName,
|
||||||
SrcSize: inf.Size,
|
SrcSize: inf.Size,
|
||||||
Header: inf.Headers,
|
Header: inf.Headers,
|
||||||
}
|
}
|
||||||
|
|
||||||
if inf, err = h.obj.CopyObject(r.Context(), params); err != nil {
|
if inf, err = h.obj.CopyObject(r.Context(), params); err != nil {
|
||||||
writeErrorCopy(w, r, h.log, "could not copy object", rid, bkt, obj, srcBucket, srcObject, err)
|
writeErrorCopy(w, reqInfo, h.log, "could not copy object", srcBucket, srcObject, err)
|
||||||
return
|
return
|
||||||
} else if err = api.EncodeToResponse(w, &CopyObjectResponse{LastModified: inf.Created.Format(time.RFC3339), ETag: inf.HashSum}); err != nil {
|
} else if err = api.EncodeToResponse(w, &CopyObjectResponse{LastModified: inf.Created.Format(time.RFC3339), ETag: inf.HashSum}); err != nil {
|
||||||
writeErrorCopy(w, r, h.log, "something went wrong", rid, bkt, obj, srcBucket, srcObject, err)
|
writeErrorCopy(w, reqInfo, h.log, "something went wrong", srcBucket, srcObject, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,20 +92,16 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
zap.Stringer("object_id", inf.ID()))
|
zap.Stringer("object_id", inf.ID()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeErrorCopy(w http.ResponseWriter, r *http.Request, log *zap.Logger, msg, rid, bkt, obj, srcBucket, srcObject string, err error) {
|
func writeErrorCopy(w http.ResponseWriter, reqInfo *api.ReqInfo, log *zap.Logger, msg, srcBucket, srcObject string, err error) {
|
||||||
log.Error(msg,
|
log.Error(msg,
|
||||||
zap.String("request_id", rid),
|
zap.String("request_id", reqInfo.RequestID),
|
||||||
zap.String("dst_bucket_name", bkt),
|
zap.String("dst_bucket_name", reqInfo.BucketName),
|
||||||
zap.String("dst_object_name", obj),
|
zap.String("dst_object_name", reqInfo.ObjectName),
|
||||||
zap.String("src_bucket_name", srcBucket),
|
zap.String("src_bucket_name", srcBucket),
|
||||||
zap.String("src_object_name", srcObject),
|
zap.String("src_object_name", srcObject),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
api.WriteErrorResponse(w, reqInfo, err)
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) {
|
func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -42,18 +41,13 @@ type DeleteObjectsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
req = mux.Vars(r)
|
|
||||||
bkt = req["bucket"]
|
|
||||||
obj = req["object"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := h.obj.DeleteObject(r.Context(), bkt, obj); err != nil {
|
if err := h.obj.DeleteObject(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil {
|
||||||
h.log.Error("could not delete object",
|
h.log.Error("could not delete object",
|
||||||
zap.String("request_id", rid),
|
zap.String("request_id", reqInfo.RequestID),
|
||||||
zap.String("bucket_name", bkt),
|
zap.String("bucket_name", reqInfo.BucketName),
|
||||||
zap.String("object_name", obj),
|
zap.String("object_name", reqInfo.ObjectName),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
|
|
||||||
// Ignore delete errors:
|
// Ignore delete errors:
|
||||||
|
@ -70,30 +64,26 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// DeleteMultipleObjectsHandler handles multiple delete requests.
|
// DeleteMultipleObjectsHandler handles multiple delete requests.
|
||||||
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
req = mux.Vars(r)
|
|
||||||
bkt = req["bucket"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
|
||||||
|
|
||||||
// Content-Md5 is requied should be set
|
// Content-Md5 is requied should be set
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
||||||
if _, ok := r.Header[api.ContentMD5]; !ok {
|
if _, ok := r.Header[api.ContentMD5]; !ok {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrMissingContentMD5), r.URL)
|
h.logAndSendError(w, "missing Content-MD5", reqInfo, api.GetAPIError(api.ErrMissingContentMD5))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content-Length is required and should be non-zero
|
// Content-Length is required and should be non-zero
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
||||||
if r.ContentLength <= 0 {
|
if r.ContentLength <= 0 {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrMissingContentLength), r.URL)
|
h.logAndSendError(w, "missing Content-Length", reqInfo, api.GetAPIError(api.ErrMissingContentLength))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal list of keys to be deleted.
|
// Unmarshal list of keys to be deleted.
|
||||||
requested := &DeleteObjectsRequest{}
|
requested := &DeleteObjectsRequest{}
|
||||||
if err := xml.NewDecoder(r.Body).Decode(requested); err != nil {
|
if err := xml.NewDecoder(r.Body).Decode(requested); err != nil {
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
h.logAndSendError(w, "couldn't decode body", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,12 +99,12 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re
|
||||||
DeletedObjects: make([]ObjectIdentifier, 0, len(toRemove)),
|
DeletedObjects: make([]ObjectIdentifier, 0, len(toRemove)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs := h.obj.DeleteObjects(r.Context(), bkt, toRemove); errs != nil && !requested.Quiet {
|
if errs := h.obj.DeleteObjects(r.Context(), reqInfo.BucketName, toRemove); errs != nil && !requested.Quiet {
|
||||||
h.log.Error("could not delete objects",
|
additional := []zap.Field{
|
||||||
zap.String("request_id", rid),
|
zap.Strings("objects_name", toRemove),
|
||||||
zap.String("bucket_name", bkt),
|
zap.Errors("errors", errs),
|
||||||
zap.Strings("object_name", toRemove),
|
}
|
||||||
zap.Errors("errors", errs))
|
h.logAndSendError(w, "could not delete objects", reqInfo, nil, additional...)
|
||||||
|
|
||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
if err, ok := e.(*api.DeleteError); ok {
|
if err, ok := e.(*api.DeleteError); ok {
|
||||||
|
@ -137,40 +127,14 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.EncodeToResponse(w, response); err != nil {
|
if err := api.EncodeToResponse(w, response); err != nil {
|
||||||
h.log.Error("could not write response",
|
h.logAndSendError(w, "could not write response", reqInfo, err, zap.Strings("objects_name", toRemove))
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.Strings("object_name", toRemove),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
rid = api.GetRequestID(r.Context())
|
if err := h.obj.DeleteBucket(r.Context(), &layer.DeleteBucketParams{Name: reqInfo.BucketName}); err != nil {
|
||||||
p = layer.DeleteBucketParams{}
|
h.logAndSendError(w, "couldn't delete bucket", reqInfo, err)
|
||||||
req = mux.Vars(r)
|
|
||||||
)
|
|
||||||
p.Name = req["bucket"]
|
|
||||||
err := h.obj.DeleteBucket(r.Context(), &p)
|
|
||||||
if err != nil {
|
|
||||||
h.log.Error("couldn't delete bucket",
|
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", p.Name),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type conditionalArgs struct {
|
type conditionalArgs struct {
|
||||||
|
@ -79,30 +77,27 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
inf *layer.ObjectInfo
|
inf *layer.ObjectInfo
|
||||||
params *layer.RangeParams
|
params *layer.RangeParams
|
||||||
|
|
||||||
req = mux.Vars(r)
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
bkt = req["bucket"]
|
|
||||||
obj = req["object"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
args, err := parseGetObjectArgs(r.Header)
|
args, err := parseGetObjectArgs(r.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, r, h.log, "could not parse request params", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not parse request params", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if inf, err = h.obj.GetObjectInfo(r.Context(), bkt, obj); err != nil {
|
if inf, err = h.obj.GetObjectInfo(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil {
|
||||||
writeError(w, r, h.log, "could not find object", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not find object", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = checkPreconditions(inf, args.Conditional); err != nil {
|
if err = checkPreconditions(inf, args.Conditional); err != nil {
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
h.logAndSendError(w, "precondition failed", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if params, err = fetchRangeHeader(r.Header, uint64(inf.Size)); err != nil {
|
if params, err = fetchRangeHeader(r.Header, uint64(inf.Size)); err != nil {
|
||||||
writeError(w, r, h.log, "could not parse range header", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not parse range header", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeHeaders(w.Header(), inf)
|
writeHeaders(w.Header(), inf)
|
||||||
|
@ -117,7 +112,7 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Range: params,
|
Range: params,
|
||||||
}
|
}
|
||||||
if err = h.obj.GetObject(r.Context(), getParams); err != nil {
|
if err = h.obj.GetObject(r.Context(), getParams); err != nil {
|
||||||
writeError(w, r, h.log, "could not get object", rid, bkt, obj, err)
|
h.logAndSendError(w, "could not get object", reqInfo, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,13 +169,3 @@ func writeRangeHeaders(w http.ResponseWriter, params *layer.RangeParams, size in
|
||||||
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", params.Start, params.End, size))
|
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", params.Start, params.End, size))
|
||||||
w.WriteHeader(http.StatusPartialContent)
|
w.WriteHeader(http.StatusPartialContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeError(w http.ResponseWriter, r *http.Request, log *zap.Logger, msg, rid, bkt, obj string, err error) {
|
|
||||||
log.Error(msg,
|
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.String("object_name", obj),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -29,20 +28,11 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
err error
|
err error
|
||||||
inf *layer.ObjectInfo
|
inf *layer.ObjectInfo
|
||||||
|
|
||||||
req = mux.Vars(r)
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
bkt = req["bucket"]
|
|
||||||
obj = req["object"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if inf, err = h.obj.GetObjectInfo(r.Context(), bkt, obj); err != nil {
|
if inf, err = h.obj.GetObjectInfo(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil {
|
||||||
h.log.Error("could not fetch object info",
|
h.logAndSendError(w, "could not fetch object info", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.String("object_name", obj),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buffer := bytes.NewBuffer(make([]byte, 0, sizeToDetectType))
|
buffer := bytes.NewBuffer(make([]byte, 0, sizeToDetectType))
|
||||||
|
@ -53,15 +43,7 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Range: getRangeToDetectContentType(inf.Size),
|
Range: getRangeToDetectContentType(inf.Size),
|
||||||
}
|
}
|
||||||
if err = h.obj.GetObject(r.Context(), getParams); err != nil {
|
if err = h.obj.GetObject(r.Context(), getParams); err != nil {
|
||||||
h.log.Error("could not get object",
|
h.logAndSendError(w, "could not get object", reqInfo, err, zap.Stringer("oid", inf.ID()))
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.String("object_name", obj),
|
|
||||||
zap.Stringer("oid", inf.ID()),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inf.ContentType = http.DetectContentType(buffer.Bytes())
|
inf.ContentType = http.DetectContentType(buffer.Bytes())
|
||||||
|
@ -70,19 +52,10 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
req = mux.Vars(r)
|
|
||||||
bkt = req["bucket"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
|
||||||
|
|
||||||
if _, err := h.obj.GetBucketInfo(r.Context(), bkt); err != nil {
|
if _, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName); err != nil {
|
||||||
h.log.Error("could not fetch object info",
|
h.logAndSendError(w, "could not fetch object info", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,40 +3,18 @@ package handler
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
bkt = mux.Vars(r)["bucket"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
|
||||||
|
|
||||||
if _, err := h.obj.GetBucketInfo(r.Context(), bkt); err != nil {
|
|
||||||
h.log.Error("something went wrong",
|
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
return
|
|
||||||
} else if err = api.EncodeToResponse(w, LocationResponse{Location: ""}); err != nil {
|
|
||||||
h.log.Error("could not write response",
|
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
|
if _, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName); err != nil {
|
||||||
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := api.EncodeToResponse(w, LocationResponse{Location: ""}); err != nil {
|
||||||
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VersioningConfiguration contains VersioningConfiguration XML representation.
|
// VersioningConfiguration contains VersioningConfiguration XML representation.
|
||||||
|
@ -26,36 +25,17 @@ type ListMultipartUploadsResult struct {
|
||||||
|
|
||||||
const maxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse.
|
const maxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse.
|
||||||
|
|
||||||
func (h *handler) registerAndSendError(w http.ResponseWriter, r *http.Request, err error, logText string) {
|
|
||||||
rid := api.GetRequestID(r.Context())
|
|
||||||
h.log.Error(logText,
|
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, err, r.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListBucketsHandler handles bucket listing requests.
|
// ListBucketsHandler handles bucket listing requests.
|
||||||
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
|
||||||
own = owner.NewID()
|
own = owner.NewID()
|
||||||
res *ListBucketsResponse
|
res *ListBucketsResponse
|
||||||
rid = api.GetRequestID(r.Context())
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
)
|
)
|
||||||
|
|
||||||
list, err := h.obj.ListBuckets(r.Context())
|
list, err := h.obj.ListBuckets(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("something went wrong",
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,58 +58,34 @@ func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = api.EncodeToResponse(w, res); err != nil {
|
if err = api.EncodeToResponse(w, res); err != nil {
|
||||||
h.log.Error("something went wrong",
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBucketVersioningHandler implements bucket versioning getter handler.
|
// GetBucketVersioningHandler implements bucket versioning getter handler.
|
||||||
func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
rid = api.GetRequestID(r.Context())
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
res = new(VersioningConfiguration)
|
res = new(VersioningConfiguration)
|
||||||
)
|
)
|
||||||
|
|
||||||
res.Xmlns = "http://s3.amazonaws.com/doc/2006-03-01/"
|
res.Xmlns = "http://s3.amazonaws.com/doc/2006-03-01/"
|
||||||
|
|
||||||
if err := api.EncodeToResponse(w, res); err != nil {
|
if err := api.EncodeToResponse(w, res); err != nil {
|
||||||
h.log.Error("something went wrong",
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListMultipartUploadsHandler implements multipart uploads listing handler.
|
// ListMultipartUploadsHandler implements multipart uploads listing handler.
|
||||||
func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
rid = api.GetRequestID(r.Context())
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
res = new(ListMultipartUploadsResult)
|
res = new(ListMultipartUploadsResult)
|
||||||
)
|
)
|
||||||
|
|
||||||
res.Xmlns = "http://s3.amazonaws.com/doc/2006-03-01/"
|
res.Xmlns = "http://s3.amazonaws.com/doc/2006-03-01/"
|
||||||
|
|
||||||
if err := api.EncodeToResponse(w, res); err != nil {
|
if err := api.EncodeToResponse(w, res); err != nil {
|
||||||
h.log.Error("something went wrong",
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,54 +3,29 @@ package handler
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported))
|
||||||
Code: api.GetAPIError(api.ErrBadRequest).Code,
|
|
||||||
Description: notSupported + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,21 +13,21 @@ import (
|
||||||
|
|
||||||
// ListObjectsV1Handler handles objects listing requests for API version 1.
|
// ListObjectsV1Handler handles objects listing requests for API version 1.
|
||||||
func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
params, err := parseListObjectsArgsV1(r)
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
|
params, err := parseListObjectsArgsV1(reqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "failed to parse arguments")
|
h.logAndSendError(w, "failed to parse arguments", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := h.obj.ListObjectsV1(r.Context(), params)
|
list, err := h.obj.ListObjectsV1(r.Context(), params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.EncodeToResponse(w, encodeV1(params, list))
|
if err = api.EncodeToResponse(w, encodeV1(params, list)); err != nil {
|
||||||
if err != nil {
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,21 +52,21 @@ func encodeV1(p *layer.ListObjectsParamsV1, list *layer.ListObjectsInfoV1) *List
|
||||||
|
|
||||||
// ListObjectsV2Handler handles objects listing requests for API version 2.
|
// ListObjectsV2Handler handles objects listing requests for API version 2.
|
||||||
func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
params, err := parseListObjectsArgsV2(r)
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
|
params, err := parseListObjectsArgsV2(reqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "failed to parse arguments")
|
h.logAndSendError(w, "failed to parse arguments", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := h.obj.ListObjectsV2(r.Context(), params)
|
list, err := h.obj.ListObjectsV2(r.Context(), params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.EncodeToResponse(w, encodeV2(params, list))
|
if err = api.EncodeToResponse(w, encodeV2(params, list)); err != nil {
|
||||||
if err != nil {
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,14 +91,13 @@ func encodeV2(p *layer.ListObjectsParamsV2, list *layer.ListObjectsInfoV2) *List
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseListObjectsArgsV1(r *http.Request) (*layer.ListObjectsParamsV1, error) {
|
func parseListObjectsArgsV1(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV1, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
|
||||||
res layer.ListObjectsParamsV1
|
res layer.ListObjectsParamsV1
|
||||||
queryValues = r.URL.Query()
|
queryValues = reqInfo.URL.Query()
|
||||||
)
|
)
|
||||||
|
|
||||||
common, err := parseListObjectArgs(r)
|
common, err := parseListObjectArgs(reqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -109,14 +108,13 @@ func parseListObjectsArgsV1(r *http.Request) (*layer.ListObjectsParamsV1, error)
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseListObjectsArgsV2(r *http.Request) (*layer.ListObjectsParamsV2, error) {
|
func parseListObjectsArgsV2(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV2, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
|
||||||
res layer.ListObjectsParamsV2
|
res layer.ListObjectsParamsV2
|
||||||
queryValues = r.URL.Query()
|
queryValues = reqInfo.URL.Query()
|
||||||
)
|
)
|
||||||
|
|
||||||
common, err := parseListObjectArgs(r)
|
common, err := parseListObjectArgs(reqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -132,17 +130,14 @@ func parseListObjectsArgsV2(r *http.Request) (*layer.ListObjectsParamsV2, error)
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseListObjectArgs(r *http.Request) (*layer.ListObjectsParamsCommon, error) {
|
func parseListObjectArgs(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsCommon, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
res layer.ListObjectsParamsCommon
|
res layer.ListObjectsParamsCommon
|
||||||
queryValues = r.URL.Query()
|
queryValues = reqInfo.URL.Query()
|
||||||
)
|
)
|
||||||
|
|
||||||
if info := api.GetReqInfo(r.Context()); info != nil {
|
res.Bucket = reqInfo.BucketName
|
||||||
res.Bucket = info.BucketName
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Delimiter = queryValues.Get("delimiter")
|
res.Delimiter = queryValues.Get("delimiter")
|
||||||
res.Encode = queryValues.Get("encoding-type")
|
res.Encode = queryValues.Get("encoding-type")
|
||||||
|
|
||||||
|
@ -204,29 +199,30 @@ func fillContents(src []*layer.ObjectInfo, encode string, fetchOwner bool) []Obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
p, err := parseListObjectVersionsRequest(r)
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
|
p, err := parseListObjectVersionsRequest(reqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "failed to parse request ")
|
h.logAndSendError(w, "failed to parse request", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := h.obj.ListObjectVersions(r.Context(), p)
|
info, err := h.obj.ListObjectVersions(r.Context(), p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response := encodeListObjectVersionsToResponse(info, p.Bucket)
|
response := encodeListObjectVersionsToResponse(info, p.Bucket)
|
||||||
if err := api.EncodeToResponse(w, response); err != nil {
|
if err = api.EncodeToResponse(w, response); err != nil {
|
||||||
h.registerAndSendError(w, r, err, "something went wrong")
|
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseListObjectVersionsRequest(r *http.Request) (*layer.ListObjectVersionsParams, error) {
|
func parseListObjectVersionsRequest(reqInfo *api.ReqInfo) (*layer.ListObjectVersionsParams, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
res layer.ListObjectVersionsParams
|
res layer.ListObjectVersionsParams
|
||||||
queryValues = r.URL.Query()
|
queryValues = reqInfo.URL.Query()
|
||||||
)
|
)
|
||||||
|
|
||||||
if queryValues.Get("max-keys") == "" {
|
if queryValues.Get("max-keys") == "" {
|
||||||
|
@ -240,10 +236,7 @@ func parseListObjectVersionsRequest(r *http.Request) (*layer.ListObjectVersionsP
|
||||||
res.Delimiter = queryValues.Get("delimiter")
|
res.Delimiter = queryValues.Get("delimiter")
|
||||||
res.Encode = queryValues.Get("encoding-type")
|
res.Encode = queryValues.Get("encoding-type")
|
||||||
res.VersionIDMarker = queryValues.Get("version-id-marker")
|
res.VersionIDMarker = queryValues.Get("version-id-marker")
|
||||||
|
res.Bucket = reqInfo.BucketName
|
||||||
if info := api.GetReqInfo(r.Context()); info != nil {
|
|
||||||
res.Bucket = info.BucketName
|
|
||||||
}
|
|
||||||
|
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/acl"
|
"github.com/nspcc-dev/neofs-api-go/pkg/acl"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
|
@ -34,35 +33,21 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
info *layer.ObjectInfo
|
info *layer.ObjectInfo
|
||||||
req = mux.Vars(r)
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
bkt = req["bucket"]
|
|
||||||
obj = req["object"]
|
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
metadata := parseMetadata(r)
|
metadata := parseMetadata(r)
|
||||||
|
|
||||||
params := &layer.PutObjectParams{
|
params := &layer.PutObjectParams{
|
||||||
Bucket: bkt,
|
Bucket: reqInfo.BucketName,
|
||||||
Object: obj,
|
Object: reqInfo.ObjectName,
|
||||||
Reader: r.Body,
|
Reader: r.Body,
|
||||||
Size: r.ContentLength,
|
Size: r.ContentLength,
|
||||||
Header: metadata,
|
Header: metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
if info, err = h.obj.PutObject(r.Context(), params); err != nil {
|
if info, err = h.obj.PutObject(r.Context(), params); err != nil {
|
||||||
h.log.Error("could not upload object",
|
h.logAndSendError(w, "could not upload object", reqInfo, err)
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.String("bucket_name", bkt),
|
|
||||||
zap.String("object_name", obj),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +69,10 @@ func parseMetadata(r *http.Request) map[string]string {
|
||||||
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
p = layer.CreateBucketParams{}
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
req = mux.Vars(r)
|
p = layer.CreateBucketParams{Name: reqInfo.BucketName}
|
||||||
)
|
)
|
||||||
p.Name = req["bucket"]
|
|
||||||
if val, ok := r.Header["X-Amz-Acl"]; ok {
|
if val, ok := r.Header["X-Amz-Acl"]; ok {
|
||||||
p.ACL, err = parseBasicACL(val[0])
|
p.ACL, err = parseBasicACL(val[0])
|
||||||
} else {
|
} else {
|
||||||
|
@ -95,19 +80,19 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "could not parse basic ACL")
|
h.logAndSendError(w, "could not parse basic ACL", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
createParams, err := parseLocationConstraint(r)
|
createParams, err := parseLocationConstraint(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "could not parse body")
|
h.logAndSendError(w, "could not parse body", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.BoxData, err = layer.GetBoxData(r.Context())
|
p.BoxData, err = layer.GetBoxData(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "could not get boxData")
|
h.logAndSendError(w, "could not get boxData", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,14 +107,14 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if p.Policy == nil {
|
if p.Policy == nil {
|
||||||
p.Policy, err = policy.Parse(defaultPolicy)
|
p.Policy, err = policy.Parse(defaultPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "could not parse policy")
|
h.logAndSendError(w, "could not parse policy", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cid, err := h.obj.CreateBucket(r.Context(), &p)
|
cid, err := h.obj.CreateBucket(r.Context(), &p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.registerAndSendError(w, r, err, "could not create bucket")
|
h.logAndSendError(w, "could not create bucket", reqInfo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,318 +3,161 @@ package handler
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented))
|
||||||
Code: "XNeoFSUnimplemented",
|
|
||||||
Description: "implement me " + mux.CurrentRoute(r).GetName(),
|
|
||||||
HTTPStatusCode: http.StatusNotImplemented,
|
|
||||||
}, r.URL)
|
|
||||||
}
|
}
|
||||||
|
|
20
api/handler/util.go
Normal file
20
api/handler/util.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo *api.ReqInfo, err error, additional ...zap.Field) {
|
||||||
|
fields := []zap.Field{zap.String("request_id", reqInfo.RequestID),
|
||||||
|
zap.String("method", reqInfo.API),
|
||||||
|
zap.String("bucket_name", reqInfo.BucketName),
|
||||||
|
zap.String("object_name", reqInfo.ObjectName),
|
||||||
|
zap.Error(err)}
|
||||||
|
fields = append(fields, additional...)
|
||||||
|
|
||||||
|
h.log.Error(logText, fields...)
|
||||||
|
api.WriteErrorResponse(w, reqInfo, err)
|
||||||
|
}
|
|
@ -49,9 +49,7 @@ func (m *maxClients) Handle(f http.HandlerFunc) http.HandlerFunc {
|
||||||
f.ServeHTTP(w, r)
|
f.ServeHTTP(w, r)
|
||||||
case <-deadline.C:
|
case <-deadline.C:
|
||||||
// Send a http timeout message
|
// Send a http timeout message
|
||||||
WriteErrorResponse(r.Context(), w,
|
WriteErrorResponse(w, GetReqInfo(r.Context()), errorCodes.ToAPIErr(ErrOperationMaxedOut))
|
||||||
errorCodes.ToAPIErr(ErrOperationMaxedOut),
|
|
||||||
r.URL)
|
|
||||||
return
|
return
|
||||||
case <-r.Context().Done():
|
case <-r.Context().Done():
|
||||||
return
|
return
|
||||||
|
|
|
@ -30,6 +30,7 @@ type (
|
||||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
||||||
BucketName string // Bucket name
|
BucketName string // Bucket name
|
||||||
ObjectName string // Object name
|
ObjectName string // Object name
|
||||||
|
URL *url.URL // Request url
|
||||||
tags []KeyVal // Any additional info not accommodated by above fields
|
tags []KeyVal // Any additional info not accommodated by above fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +137,7 @@ func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqI
|
||||||
RemoteHost: GetSourceIP(r),
|
RemoteHost: GetSourceIP(r),
|
||||||
RequestID: GetRequestID(w),
|
RequestID: GetRequestID(w),
|
||||||
DeploymentID: deploymentID.String(),
|
DeploymentID: deploymentID.String(),
|
||||||
|
URL: r.URL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +196,9 @@ func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context {
|
||||||
// GetReqInfo returns ReqInfo if set.
|
// GetReqInfo returns ReqInfo if set.
|
||||||
func GetReqInfo(ctx context.Context) *ReqInfo {
|
func GetReqInfo(ctx context.Context) *ReqInfo {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil
|
return &ReqInfo{}
|
||||||
} else if r, ok := ctx.Value(ctxRequestInfo).(*ReqInfo); ok {
|
} else if r, ok := ctx.Value(ctxRequestInfo).(*ReqInfo); ok {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
return nil
|
return &ReqInfo{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,9 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -119,7 +117,7 @@ var s3ErrorResponseMap = map[string]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteErrorResponse writes error headers.
|
// WriteErrorResponse writes error headers.
|
||||||
func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err error, reqURL *url.URL) {
|
func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) {
|
||||||
code := http.StatusInternalServerError
|
code := http.StatusInternalServerError
|
||||||
|
|
||||||
if e, ok := err.(Error); ok {
|
if e, ok := err.(Error); ok {
|
||||||
|
@ -136,8 +134,7 @@ func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err error, r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate error response.
|
// Generate error response.
|
||||||
errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path,
|
errorResponse := getAPIErrorResponse(reqInfo, err)
|
||||||
w.Header().Get(hdrAmzRequestID), deploymentID.String())
|
|
||||||
encodedErrorResponse := EncodeResponse(errorResponse)
|
encodedErrorResponse := EncodeResponse(errorResponse)
|
||||||
WriteResponse(w, code, encodedErrorResponse, MimeXML)
|
WriteResponse(w, code, encodedErrorResponse, MimeXML)
|
||||||
}
|
}
|
||||||
|
@ -145,11 +142,11 @@ func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err error, r
|
||||||
// If none of the http routes match respond with appropriate errors.
|
// If none of the http routes match respond with appropriate errors.
|
||||||
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
|
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
|
desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
|
||||||
WriteErrorResponse(r.Context(), w, Error{
|
WriteErrorResponse(w, GetReqInfo(r.Context()), Error{
|
||||||
Code: "XMinioUnknownAPIRequest",
|
Code: "XMinioUnknownAPIRequest",
|
||||||
Description: desc,
|
Description: desc,
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
}, r.URL)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write http common headers.
|
// Write http common headers.
|
||||||
|
|
|
@ -27,7 +27,7 @@ func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) {
|
||||||
ctx = r.Context()
|
ctx = r.Context()
|
||||||
} else {
|
} else {
|
||||||
log.Error("failed to pass authentication", zap.Error(err))
|
log.Error("failed to pass authentication", zap.Error(err))
|
||||||
WriteErrorResponse(r.Context(), w, GetAPIError(ErrAccessDenied), r.URL)
|
WriteErrorResponse(w, GetReqInfo(r.Context()), GetAPIError(ErrAccessDenied))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue