[#184] Unify error handling

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-08-05 12:18:52 +03:00
parent 6674e350cc
commit f4c29cd300
17 changed files with 196 additions and 541 deletions

View file

@ -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 Gate",
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,
} }
} }

View file

@ -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.

View file

@ -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) {

View file

@ -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)
} }
} }

View file

@ -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)
}

View file

@ -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
} }

View file

@ -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)
}
} }

View file

@ -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)
} }
} }

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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
View 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)
}

View file

@ -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

View file

@ -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{}
} }

View file

@ -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.

View file

@ -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 {