frostfs-s3-gw/api/handler/delete.go

183 lines
5 KiB
Go
Raw Normal View History

package handler
import (
"encoding/xml"
"net/http"
"github.com/gorilla/mux"
"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"go.uber.org/zap"
"google.golang.org/grpc/status"
)
// DeleteObjectsRequest - xml carrying the object key names which needs to be deleted.
type DeleteObjectsRequest struct {
// Element to enable quiet mode for the request
Quiet bool
// List of objects to be deleted
Objects []ObjectIdentifier `xml:"Object"`
}
// ObjectIdentifier carries key name for the object to delete.
type ObjectIdentifier struct {
ObjectName string `xml:"Key"`
}
// DeleteError structure.
type DeleteError struct {
Code string
Message string
Key string
}
// DeleteObjectsResponse container for multiple object deletes.
type DeleteObjectsResponse struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"`
// Collection of all deleted objects
DeletedObjects []ObjectIdentifier `xml:"Deleted,omitempty"`
// Collection of errors deleting certain objects.
Errors []DeleteError `xml:"Error,omitempty"`
}
func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
var (
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 {
h.log.Error("could not delete object",
zap.String("request_id", rid),
zap.String("bucket_name", bkt),
zap.String("object_name", obj),
zap.Error(err))
// Ignore delete errors:
// api.WriteErrorResponse(r.Context(), w, api.Error{
// Code: api.GetAPIError(api.ErrInternalError).Code,
// Description: err.Error(),
// HTTPStatusCode: http.StatusInternalServerError,
// }, r.URL)
}
w.WriteHeader(http.StatusNoContent)
}
2021-05-13 19:25:32 +00:00
// DeleteMultipleObjectsHandler handles multiple delete requests.
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
var (
req = mux.Vars(r)
bkt = req["bucket"]
rid = api.GetRequestID(r.Context())
)
// Content-Md5 is requied should be set
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if _, ok := r.Header[api.ContentMD5]; !ok {
api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrMissingContentMD5), r.URL)
return
}
// Content-Length is required and should be non-zero
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if r.ContentLength <= 0 {
api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrMissingContentLength), r.URL)
return
}
// Unmarshal list of keys to be deleted.
requested := &DeleteObjectsRequest{}
if err := xml.NewDecoder(r.Body).Decode(requested); err != nil {
api.WriteErrorResponse(r.Context(), w, err, r.URL)
return
}
removed := make(map[string]struct{})
toRemove := make([]string, 0, len(requested.Objects))
for _, obj := range requested.Objects {
removed[obj.ObjectName] = struct{}{}
toRemove = append(toRemove, obj.ObjectName)
}
response := &DeleteObjectsResponse{
Errors: make([]DeleteError, 0, len(toRemove)),
DeletedObjects: make([]ObjectIdentifier, 0, len(toRemove)),
}
if errs := h.obj.DeleteObjects(r.Context(), bkt, toRemove); errs != nil && !requested.Quiet {
h.log.Error("could not delete objects",
zap.String("request_id", rid),
zap.String("bucket_name", bkt),
zap.Strings("object_name", toRemove),
zap.Errors("errors", errs))
for _, e := range errs {
if err, ok := e.(*api.DeleteError); ok {
code := "BadRequest"
desc := err.Error()
if st, ok := status.FromError(err.Err); ok && st != nil {
desc = st.Message()
code = st.Code().String()
}
response.Errors = append(response.Errors, DeleteError{
Code: code,
Message: desc,
Key: err.Object,
})
delete(removed, err.Object)
}
}
}
for key := range removed {
response.DeletedObjects = append(response.DeletedObjects, ObjectIdentifier{ObjectName: key})
}
if err := api.EncodeToResponse(w, response); err != nil {
h.log.Error("could not write response",
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
}
}
func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
var (
rid = api.GetRequestID(r.Context())
p = layer.DeleteBucketParams{}
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)
}
}