KirillovDenis/feature/use_chi_instead_of_gorilla #149
46 changed files with 1039 additions and 938 deletions
|
@ -19,6 +19,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
|
@ -244,7 +245,7 @@ func (s *statement) UnmarshalJSON(data []byte) error {
|
|||
|
||||
func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -258,7 +259,7 @@ func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, h.encodeBucketACL(ctx, bktInfo.Name, bucketACL)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, h.encodeBucketACL(ctx, bktInfo.Name, bucketACL)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ func (h *handler) bearerTokenIssuerKey(ctx context.Context) (*keys.PublicKey, er
|
|||
}
|
||||
|
||||
func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
key, err := h.bearerTokenIssuerKey(r.Context())
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "couldn't get bearer token issuer key", reqInfo, err)
|
||||
|
@ -367,7 +368,7 @@ func (h *handler) updateBucketACL(r *http.Request, astChild *ast, bktInfo *data.
|
|||
|
||||
func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -393,14 +394,14 @@ func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, h.encodeObjectACL(ctx, bucketACL, reqInfo.BucketName, objInfo.VersionID())); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, h.encodeObjectACL(ctx, bucketACL, reqInfo.BucketName, objInfo.VersionID())); err != nil {
|
||||
h.logAndSendError(w, "failed to encode response", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
versionID := reqInfo.URL.Query().Get(api.QueryVersionID)
|
||||
key, err := h.bearerTokenIssuerKey(ctx)
|
||||
if err != nil {
|
||||
|
@ -476,7 +477,7 @@ func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -514,7 +515,7 @@ func checkOwner(info *data.BucketInfo, owner string) error {
|
|||
}
|
||||
|
||||
func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
|
@ -1427,7 +1428,7 @@ func TestPutBucketPolicy(t *testing.T) {
|
|||
createBucket(t, hc, bktName, box)
|
||||
|
||||
w, r := prepareTestPayloadRequest(hc, bktName, "", bytes.NewReader([]byte(bktPolicy)))
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx := context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().PutBucketPolicyHandler(w, r)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
|
@ -1449,7 +1450,7 @@ func putBucketPolicy(hc *handlerContext, bktName string, bktPolicy *bucketPolicy
|
|||
require.NoError(hc.t, err)
|
||||
|
||||
w, r := prepareTestPayloadRequest(hc, bktName, "", bytes.NewReader(body))
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx := context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().PutBucketPolicyHandler(w, r)
|
||||
assertStatus(hc.t, w, status)
|
||||
|
@ -1516,7 +1517,7 @@ func createBucketAssertS3Error(hc *handlerContext, bktName string, box *accessbo
|
|||
|
||||
func createBucketBase(hc *handlerContext, bktName string, box *accessbox.Box) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx := context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().CreateBucketHandler(w, r)
|
||||
return w
|
||||
|
@ -1527,7 +1528,7 @@ func putBucketACL(t *testing.T, tc *handlerContext, bktName string, box *accessb
|
|||
for key, val := range header {
|
||||
r.Header.Set(key, val)
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx := context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
tc.Handler().PutBucketACLHandler(w, r)
|
||||
assertStatus(t, w, http.StatusOK)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -67,7 +68,7 @@ var validAttributes = map[string]struct{}{
|
|||
}
|
||||
|
||||
func (h *handler) GetObjectAttributesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
params, err := parseGetObjectAttributeArgs(r)
|
||||
if err != nil {
|
||||
|
@ -123,7 +124,7 @@ func (h *handler) GetObjectAttributesHandler(w http.ResponseWriter, r *http.Requ
|
|||
}
|
||||
|
||||
writeAttributesHeaders(w.Header(), extendedInfo, bktSettings.Unversioned())
|
||||
if err = api.EncodeToResponse(w, response); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, response); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -47,7 +48,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sessionTokenEACL *session.Container
|
||||
|
||||
ctx = r.Context()
|
||||
reqInfo = api.GetReqInfo(ctx)
|
||||
reqInfo = middleware.GetReqInfo(ctx)
|
||||
|
||||
containsACL = containsACLHeaders(r)
|
||||
)
|
||||
|
@ -198,7 +199,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
dstObjInfo := extendedDstObjInfo.ObjectInfo
|
||||
|
||||
if err = api.EncodeToResponse(w, &CopyObjectResponse{LastModified: dstObjInfo.Created.UTC().Format(time.RFC3339), ETag: dstObjInfo.HashSum}); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, &CopyObjectResponse{LastModified: dstObjInfo.Created.UTC().Format(time.RFC3339), ETag: dstObjInfo.HashSum}); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err, additional...)
|
||||
return
|
||||
}
|
||||
|
@ -255,7 +256,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func isCopyingToItselfForbidden(reqInfo *api.ReqInfo, srcBucket string, srcObject string, settings *data.BucketSettings, args *copyObjectArgs) bool {
|
||||
func isCopyingToItselfForbidden(reqInfo *middleware.ReqInfo, srcBucket string, srcObject string, settings *data.BucketSettings, args *copyObjectArgs) bool {
|
||||
if reqInfo.BucketName != srcBucket || reqInfo.ObjectName != srcObject {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ const (
|
|||
)
|
||||
|
||||
func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -32,14 +33,14 @@ func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, cors); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, cors); err != nil {
|
||||
h.logAndSendError(w, "could not encode cors to response", reqInfo, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -63,11 +64,11 @@ func (h *handler) PutBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
api.WriteSuccessResponseHeadersOnly(w)
|
||||
middleware.WriteSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -92,7 +93,7 @@ func (h *handler) AppendCORSHeaders(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
if reqInfo.BucketName == "" {
|
||||
return
|
||||
}
|
||||
|
@ -143,7 +144,7 @@ func (h *handler) AppendCORSHeaders(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *handler) Preflight(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
bktInfo, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not get bucket info", reqInfo, err)
|
||||
|
@ -197,7 +198,7 @@ func (h *handler) Preflight(w http.ResponseWriter, r *http.Request) {
|
|||
if o != wildcard {
|
||||
w.Header().Set(api.AccessControlAllowCredentials, "true")
|
||||
}
|
||||
api.WriteSuccessResponseHeadersOnly(w)
|
||||
middleware.WriteSuccessResponseHeadersOnly(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
func TestCORSOriginWildcard(t *testing.T) {
|
||||
|
@ -23,14 +24,14 @@ func TestCORSOriginWildcard(t *testing.T) {
|
|||
bktName := "bucket-for-cors"
|
||||
box, _ := createAccessBox(t)
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx := context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
r.Header.Add(api.AmzACL, "public-read")
|
||||
hc.Handler().CreateBucketHandler(w, r)
|
||||
assertStatus(t, w, http.StatusOK)
|
||||
|
||||
w, r = prepareTestPayloadRequest(hc, bktName, "", strings.NewReader(body))
|
||||
ctx = context.WithValue(r.Context(), api.BoxData, box)
|
||||
ctx = context.WithValue(r.Context(), middleware.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().PutBucketCorsHandler(w, r)
|
||||
assertStatus(t, w, http.StatusOK)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
|
@ -62,7 +63,7 @@ type DeleteObjectsResponse struct {
|
|||
|
||||
func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
versionID := reqInfo.URL.Query().Get(api.QueryVersionID)
|
||||
versionedObject := []*layer.VersionedObject{{
|
||||
Name: reqInfo.ObjectName,
|
||||
|
@ -158,7 +159,7 @@ func isErrObjectLocked(err error) bool {
|
|||
// DeleteMultipleObjectsHandler handles multiple delete requests.
|
||||
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
|
||||
// Content-Md5 is required and should be set
|
||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
||||
|
@ -264,14 +265,14 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re
|
|||
h.reqLogger(ctx).Error("couldn't delete objects", fields...)
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, response); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, response); err != nil {
|
||||
h.logAndSendError(w, "could not write response", reqInfo, err, zap.Array("objects", marshaler))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not get bucket info", reqInfo, err)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -117,7 +118,7 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var (
|
||||
params *layer.RangeParams
|
||||
|
||||
reqInfo = api.GetReqInfo(r.Context())
|
||||
reqInfo = middleware.GetReqInfo(r.Context())
|
||||
)
|
||||
|
||||
conditional, err := parseConditionalHeaders(r.Header)
|
||||
|
|
|
@ -13,10 +13,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/resolver"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
|
@ -139,7 +139,7 @@ func prepareHandlerContextBase(t *testing.T, minCache bool) *handlerContext {
|
|||
h: h,
|
||||
tp: tp,
|
||||
tree: treeMock,
|
||||
context: context.WithValue(context.Background(), api.BoxData, newTestAccessBox(t, key)),
|
||||
context: context.WithValue(context.Background(), middleware.BoxData, newTestAccessBox(t, key)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,8 +246,8 @@ func prepareTestRequestWithQuery(hc *handlerContext, bktName, objName string, qu
|
|||
r := httptest.NewRequest(http.MethodPut, defaultURL, bytes.NewReader(body))
|
||||
r.URL.RawQuery = query.Encode()
|
||||
|
||||
reqInfo := api.NewReqInfo(w, r, api.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
r = r.WithContext(api.SetReqInfo(hc.Context(), reqInfo))
|
||||
reqInfo := middleware.NewReqInfo(w, r, middleware.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
r = r.WithContext(middleware.SetReqInfo(hc.Context(), reqInfo))
|
||||
|
||||
return w, r
|
||||
}
|
||||
|
@ -256,8 +256,8 @@ func prepareTestPayloadRequest(hc *handlerContext, bktName, objName string, payl
|
|||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodPut, defaultURL, payload)
|
||||
|
||||
reqInfo := api.NewReqInfo(w, r, api.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
r = r.WithContext(api.SetReqInfo(hc.Context(), reqInfo))
|
||||
reqInfo := middleware.NewReqInfo(w, r, middleware.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
r = r.WithContext(middleware.SetReqInfo(hc.Context(), reqInfo))
|
||||
|
||||
return w, r
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -26,7 +27,7 @@ func getRangeToDetectContentType(maxSize uint64) *layer.RangeParams {
|
|||
}
|
||||
|
||||
func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -114,7 +115,7 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -131,7 +132,7 @@ func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Set(api.ContainerZone, bktInfo.Zone)
|
||||
}
|
||||
|
||||
api.WriteResponse(w, http.StatusOK, nil, api.MimeNone)
|
||||
middleware.WriteResponse(w, http.StatusOK, nil, middleware.MimeNone)
|
||||
}
|
||||
|
||||
func (h *handler) setLockingHeaders(bktInfo *data.BucketInfo, lockInfo *data.LockInfo, header http.Header) error {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
|
@ -84,7 +85,7 @@ func TestInvalidAccessThroughCache(t *testing.T) {
|
|||
headObject(t, tc, bktName, objName, nil, http.StatusOK)
|
||||
|
||||
w, r := prepareTestRequest(tc, bktName, objName, nil)
|
||||
tc.Handler().HeadObjectHandler(w, r.WithContext(context.WithValue(r.Context(), api.BoxData, newTestAccessBox(t, nil))))
|
||||
tc.Handler().HeadObjectHandler(w, r.WithContext(context.WithValue(r.Context(), middleware.BoxData, newTestAccessBox(t, nil))))
|
||||
assertStatus(t, w, http.StatusForbidden)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -15,7 +15,7 @@ func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Reques
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, LocationResponse{Location: bktInfo.LocationConstraint}); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, LocationResponse{Location: bktInfo.LocationConstraint}); err != nil {
|
||||
h.logAndSendError(w, "couldn't encode bucket location response", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
|
@ -15,7 +15,7 @@ func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var (
|
||||
own user.ID
|
||||
res *ListBucketsResponse
|
||||
reqInfo = api.GetReqInfo(r.Context())
|
||||
reqInfo = middleware.GetReqInfo(r.Context())
|
||||
)
|
||||
|
||||
list, err := h.obj.ListBuckets(r.Context())
|
||||
|
@ -42,7 +42,7 @@ func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, res); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, res); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
apiErrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,7 +27,7 @@ const (
|
|||
)
|
||||
|
||||
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -73,7 +74,7 @@ func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *htt
|
|||
}
|
||||
|
||||
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -100,13 +101,13 @@ func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *htt
|
|||
settings.LockConfiguration.ObjectLockEnabled = enabledValue
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, settings.LockConfiguration); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, settings.LockConfiguration); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -158,7 +159,7 @@ func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -189,13 +190,13 @@ func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque
|
|||
legalHold.Status = legalHoldOn
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, legalHold); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, legalHold); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -242,7 +243,7 @@ func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -281,7 +282,7 @@ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
|
|||
retention.Mode = complianceMode
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, retention); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, retention); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
apiErrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -313,7 +314,7 @@ func TestPutBucketLockConfigurationHandler(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodPut, defaultURL, bytes.NewReader(body))
|
||||
r = r.WithContext(api.SetReqInfo(r.Context(), api.NewReqInfo(w, r, api.ObjectRequest{Bucket: tc.bucket})))
|
||||
r = r.WithContext(middleware.SetReqInfo(r.Context(), middleware.NewReqInfo(w, r, middleware.ObjectRequest{Bucket: tc.bucket})))
|
||||
|
||||
hc.Handler().PutBucketObjectLockConfigHandler(w, r)
|
||||
|
||||
|
@ -386,7 +387,7 @@ func TestGetBucketLockConfigurationHandler(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodPut, defaultURL, bytes.NewReader(nil))
|
||||
r = r.WithContext(api.SetReqInfo(r.Context(), api.NewReqInfo(w, r, api.ObjectRequest{Bucket: tc.bucket})))
|
||||
r = r.WithContext(middleware.SetReqInfo(r.Context(), middleware.NewReqInfo(w, r, middleware.ObjectRequest{Bucket: tc.bucket})))
|
||||
|
||||
hc.Handler().GetBucketObjectLockConfigHandler(w, r)
|
||||
|
||||
|
@ -406,7 +407,7 @@ func TestGetBucketLockConfigurationHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
func assertS3Error(t *testing.T, w *httptest.ResponseRecorder, expectedError apiErrors.Error) {
|
||||
actualErrorResponse := &api.ErrorResponse{}
|
||||
actualErrorResponse := &middleware.ErrorResponse{}
|
||||
err := xml.NewDecoder(w.Result().Body).Decode(actualErrorResponse)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -94,7 +95,7 @@ const (
|
|||
)
|
||||
|
||||
func (h *handler) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -170,7 +171,7 @@ func (h *handler) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Re
|
|||
UploadID: uploadID.String(),
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, resp); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, resp); err != nil {
|
||||
h.logAndSendError(w, "could not encode InitiateMultipartUploadResponse to response", reqInfo, err, additional...)
|
||||
return
|
||||
}
|
||||
|
@ -196,7 +197,7 @@ func formACLHeadersForMultipart(header http.Header) map[string]string {
|
|||
}
|
||||
|
||||
func (h *handler) UploadPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -270,14 +271,14 @@ func (h *handler) UploadPartHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
w.Header().Set(api.ETag, hash)
|
||||
api.WriteSuccessResponseHeadersOnly(w)
|
||||
middleware.WriteSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func (h *handler) UploadPartCopy(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
versionID string
|
||||
ctx = r.Context()
|
||||
reqInfo = api.GetReqInfo(ctx)
|
||||
reqInfo = middleware.GetReqInfo(ctx)
|
||||
queryValues = reqInfo.URL.Query()
|
||||
uploadID = queryValues.Get(uploadIDHeaderName)
|
||||
additional = []zap.Field{zap.String("uploadID", uploadID), zap.String("Key", reqInfo.ObjectName)}
|
||||
|
@ -385,13 +386,13 @@ func (h *handler) UploadPartCopy(w http.ResponseWriter, r *http.Request) {
|
|||
addSSECHeaders(w.Header(), r.Header)
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, response); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, response); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -438,11 +439,11 @@ func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.
|
|||
// successfully or not.
|
||||
headerIsWritten := stopPeriodicResponseWriter()
|
||||
|
||||
responseWriter := api.EncodeToResponse
|
||||
responseWriter := middleware.EncodeToResponse
|
||||
errLogger := h.logAndSendError
|
||||
// Do not send XML and HTTP headers if periodic writer was invoked at this point.
|
||||
if headerIsWritten {
|
||||
responseWriter = api.EncodeToResponseNoHeader
|
||||
responseWriter = middleware.EncodeToResponseNoHeader
|
||||
errLogger = h.logAndSendErrorNoHeader
|
||||
}
|
||||
|
||||
|
@ -466,7 +467,7 @@ func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.
|
|||
}
|
||||
}
|
||||
|
||||
func (h *handler) completeMultipartUpload(r *http.Request, c *layer.CompleteMultipartParams, bktInfo *data.BucketInfo, reqInfo *api.ReqInfo) (*data.ObjectInfo, error) {
|
||||
func (h *handler) completeMultipartUpload(r *http.Request, c *layer.CompleteMultipartParams, bktInfo *data.BucketInfo, reqInfo *middleware.ReqInfo) (*data.ObjectInfo, error) {
|
||||
ctx := r.Context()
|
||||
uploadData, extendedObjInfo, err := h.obj.CompleteMultipartUpload(ctx, c)
|
||||
if err != nil {
|
||||
|
@ -530,7 +531,7 @@ func (h *handler) completeMultipartUpload(r *http.Request, c *layer.CompleteMult
|
|||
}
|
||||
|
||||
func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -572,13 +573,13 @@ func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Req
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeListMultipartUploadsToResponse(list, p)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeListMultipartUploadsToResponse(list, p)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) ListPartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -635,13 +636,13 @@ func (h *handler) ListPartsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeListPartsToResponse(list, p)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeListPartsToResponse(list, p)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
|
|
@ -3,18 +3,18 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
h.logAndSendError(w, "not supported", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
h.logAndSendError(w, "not supported", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
h.logAndSendError(w, "not supported", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported))
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ type (
|
|||
Event string
|
||||
NotificationInfo *data.NotificationInfo
|
||||
BktInfo *data.BucketInfo
|
||||
ReqInfo *api.ReqInfo
|
||||
ReqInfo *middleware.ReqInfo
|
||||
User string
|
||||
Time time.Time
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ var validEvents = map[string]struct{}{
|
|||
}
|
||||
|
||||
func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not get bucket info", reqInfo, err)
|
||||
|
@ -133,7 +133,7 @@ func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
|
||||
func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -147,7 +147,7 @@ func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Re
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, conf); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, conf); err != nil {
|
||||
h.logAndSendError(w, "could not encode bucket notification configuration to response", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func (h *handler) sendNotifications(ctx context.Context, p *SendNotificationPara
|
|||
}
|
||||
|
||||
// checkBucketConfiguration checks notification configuration and generates an ID for configurations with empty ids.
|
||||
func (h *handler) checkBucketConfiguration(ctx context.Context, conf *data.NotificationConfiguration, r *api.ReqInfo) (completed bool, err error) {
|
||||
func (h *handler) checkBucketConfiguration(ctx context.Context, conf *data.NotificationConfiguration, r *middleware.ReqInfo) (completed bool, err error) {
|
||||
if conf == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
// ListObjectsV1Handler handles objects listing requests for API version 1.
|
||||
func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
params, err := parseListObjectsArgsV1(reqInfo)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "failed to parse arguments", reqInfo, err)
|
||||
|
@ -33,7 +33,7 @@ func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeV1(params, list)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeV1(params, list)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func encodeV1(p *layer.ListObjectsParamsV1, list *layer.ListObjectsInfoV1) *List
|
|||
|
||||
// ListObjectsV2Handler handles objects listing requests for API version 2.
|
||||
func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
params, err := parseListObjectsArgsV2(reqInfo)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "failed to parse arguments", reqInfo, err)
|
||||
|
@ -77,7 +77,7 @@ func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeV2(params, list)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeV2(params, list)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func encodeV2(p *layer.ListObjectsParamsV2, list *layer.ListObjectsInfoV2) *List
|
|||
return res
|
||||
}
|
||||
|
||||
func parseListObjectsArgsV1(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV1, error) {
|
||||
func parseListObjectsArgsV1(reqInfo *middleware.ReqInfo) (*layer.ListObjectsParamsV1, error) {
|
||||
var (
|
||||
res layer.ListObjectsParamsV1
|
||||
queryValues = reqInfo.URL.Query()
|
||||
|
@ -120,7 +120,7 @@ func parseListObjectsArgsV1(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV1, e
|
|||
return &res, nil
|
||||
}
|
||||
|
||||
func parseListObjectsArgsV2(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV2, error) {
|
||||
func parseListObjectsArgsV2(reqInfo *middleware.ReqInfo) (*layer.ListObjectsParamsV2, error) {
|
||||
var (
|
||||
res layer.ListObjectsParamsV2
|
||||
queryValues = reqInfo.URL.Query()
|
||||
|
@ -142,7 +142,7 @@ func parseListObjectsArgsV2(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsV2, e
|
|||
return &res, nil
|
||||
}
|
||||
|
||||
func parseListObjectArgs(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsCommon, error) {
|
||||
func parseListObjectArgs(reqInfo *middleware.ReqInfo) (*layer.ListObjectsParamsCommon, error) {
|
||||
var (
|
||||
err error
|
||||
res layer.ListObjectsParamsCommon
|
||||
|
@ -211,7 +211,7 @@ func fillContents(src []*data.ObjectInfo, encode string, fetchOwner bool) []Obje
|
|||
}
|
||||
|
||||
func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
p, err := parseListObjectVersionsRequest(reqInfo)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "failed to parse request", reqInfo, err)
|
||||
|
@ -230,12 +230,12 @@ func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http
|
|||
}
|
||||
|
||||
response := encodeListObjectVersionsToResponse(info, p.BktInfo.Name)
|
||||
if err = api.EncodeToResponse(w, response); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, response); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseListObjectVersionsRequest(reqInfo *api.ReqInfo) (*layer.ListObjectVersionsParams, error) {
|
||||
func parseListObjectVersionsRequest(reqInfo *middleware.ReqInfo) (*layer.ListObjectVersionsParams, error) {
|
||||
var (
|
||||
err error
|
||||
res layer.ListObjectVersionsParams
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
|
@ -180,7 +181,7 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sessionTokenEACL *session.Container
|
||||
containsACL = containsACLHeaders(r)
|
||||
ctx = r.Context()
|
||||
reqInfo = api.GetReqInfo(ctx)
|
||||
reqInfo = middleware.GetReqInfo(ctx)
|
||||
)
|
||||
|
||||
if containsACL {
|
||||
|
@ -335,7 +336,7 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
w.Header().Set(api.ETag, objInfo.HashSum)
|
||||
api.WriteSuccessResponseHeadersOnly(w)
|
||||
middleware.WriteSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func formEncryptionParams(r *http.Request) (enc encryption.Params, err error) {
|
||||
|
@ -388,7 +389,7 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
|
|||
tagSet map[string]string
|
||||
sessionTokenEACL *session.Container
|
||||
ctx = r.Context()
|
||||
reqInfo = api.GetReqInfo(ctx)
|
||||
reqInfo = middleware.GetReqInfo(ctx)
|
||||
metadata = make(map[string]string)
|
||||
containsACL = containsACLHeaders(r)
|
||||
)
|
||||
|
@ -530,7 +531,7 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
|
|||
ETag: objInfo.HashSum,
|
||||
}
|
||||
w.WriteHeader(status)
|
||||
if _, err = w.Write(api.EncodeResponse(resp)); err != nil {
|
||||
if _, err = w.Write(middleware.EncodeResponse(resp)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
return
|
||||
|
@ -541,7 +542,7 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(status)
|
||||
}
|
||||
|
||||
func checkPostPolicy(r *http.Request, reqInfo *api.ReqInfo, metadata map[string]string) (*postPolicy, error) {
|
||||
func checkPostPolicy(r *http.Request, reqInfo *middleware.ReqInfo, metadata map[string]string) (*postPolicy, error) {
|
||||
policy := &postPolicy{empty: true}
|
||||
if policyStr := auth.MultipartFormValue(r, "policy"); policyStr != "" {
|
||||
policyData, err := base64.StdEncoding.DecodeString(policyStr)
|
||||
|
@ -681,7 +682,7 @@ func parseMetadata(r *http.Request) map[string]string {
|
|||
|
||||
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
p := &layer.CreateBucketParams{
|
||||
Name: reqInfo.BucketName,
|
||||
}
|
||||
|
@ -761,7 +762,7 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
api.WriteSuccessResponseHeadersOnly(w)
|
||||
middleware.WriteSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func (h handler) setPolicy(prm *layer.CreateBucketParams, locationConstraint string, userPolicies []*accessbox.ContainerPolicy) error {
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4"
|
||||
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -110,7 +111,7 @@ func TestEmptyPostPolicy(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
reqInfo := &api.ReqInfo{}
|
||||
reqInfo := &middleware.ReqInfo{}
|
||||
metadata := make(map[string]string)
|
||||
|
||||
_, err := checkPostPolicy(r, reqInfo, metadata)
|
||||
|
@ -217,16 +218,16 @@ func TestPutObjectWithStreamBodyAWSExample(t *testing.T) {
|
|||
req.Body = io.NopCloser(reqBody)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
reqInfo := api.NewReqInfo(w, req, api.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
req = req.WithContext(api.SetReqInfo(tc.Context(), reqInfo))
|
||||
req = req.WithContext(context.WithValue(req.Context(), api.ClientTime, signTime))
|
||||
req = req.WithContext(context.WithValue(req.Context(), api.AuthHeaders, &auth.AuthHeader{
|
||||
reqInfo := middleware.NewReqInfo(w, req, middleware.ObjectRequest{Bucket: bktName, Object: objName})
|
||||
req = req.WithContext(middleware.SetReqInfo(tc.Context(), reqInfo))
|
||||
req = req.WithContext(context.WithValue(req.Context(), middleware.ClientTime, signTime))
|
||||
req = req.WithContext(context.WithValue(req.Context(), middleware.AuthHeaders, &auth.AuthHeader{
|
||||
AccessKeyID: AWSAccessKeyID,
|
||||
SignatureV4: "4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9",
|
||||
Service: "s3",
|
||||
Region: "us-east-1",
|
||||
}))
|
||||
req = req.WithContext(context.WithValue(req.Context(), api.BoxData, &accessbox.Box{
|
||||
req = req.WithContext(context.WithValue(req.Context(), middleware.BoxData, &accessbox.Box{
|
||||
Gate: &accessbox.GateData{
|
||||
AccessKey: AWSSecretAccessKey,
|
||||
},
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||
v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4"
|
||||
errs "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
)
|
||||
|
@ -193,12 +193,12 @@ func (c *s3ChunkReader) Read(buf []byte) (num int, err error) {
|
|||
func newSignV4ChunkedReader(req *http.Request) (io.ReadCloser, error) {
|
||||
// Expecting to refactor this in future:
|
||||
// https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/issues/137
|
||||
box, ok := req.Context().Value(api.BoxData).(*accessbox.Box)
|
||||
box, ok := req.Context().Value(middleware.BoxData).(*accessbox.Box)
|
||||
if !ok {
|
||||
return nil, errs.GetAPIError(errs.ErrAuthorizationHeaderMalformed)
|
||||
}
|
||||
|
||||
authHeaders, ok := req.Context().Value(api.AuthHeaders).(*auth.AuthHeader)
|
||||
authHeaders, ok := req.Context().Value(middleware.AuthHeaders).(*auth.AuthHeader)
|
||||
if !ok {
|
||||
return nil, errs.GetAPIError(errs.ErrAuthorizationHeaderMalformed)
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ func newSignV4ChunkedReader(req *http.Request) (io.ReadCloser, error) {
|
|||
return nil, errs.GetAPIError(errs.ErrSignatureDoesNotMatch)
|
||||
}
|
||||
|
||||
reqTime, ok := req.Context().Value(api.ClientTime).(time.Time)
|
||||
reqTime, ok := req.Context().Value(middleware.ClientTime).(time.Time)
|
||||
if !ok {
|
||||
return nil, errs.GetAPIError(errs.ErrMalformedDate)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -25,7 +26,7 @@ const (
|
|||
|
||||
func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
|
||||
tagSet, err := readTagSet(r.Body)
|
||||
if err != nil {
|
||||
|
@ -72,7 +73,7 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
}
|
||||
|
||||
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -103,14 +104,14 @@ func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
if settings.VersioningEnabled() {
|
||||
w.Header().Set(api.AmzVersionID, versionID)
|
||||
}
|
||||
if err = api.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
reqInfo := api.GetReqInfo(ctx)
|
||||
reqInfo := middleware.GetReqInfo(ctx)
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -149,7 +150,7 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ
|
|||
}
|
||||
|
||||
func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
tagSet, err := readTagSet(r.Body)
|
||||
if err != nil {
|
||||
|
@ -170,7 +171,7 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
}
|
||||
|
||||
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -184,14 +185,14 @@ func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
|
|
@ -3,58 +3,58 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
h.logAndSendError(w, "not implemented", middleware.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||
}
|
||||
|
|
|
@ -11,21 +11,22 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
frosterrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (h *handler) reqLogger(ctx context.Context) *zap.Logger {
|
||||
reqLogger := api.GetReqLog(ctx)
|
||||
reqLogger := middleware.GetReqLog(ctx)
|
||||
if reqLogger != nil {
|
||||
return reqLogger
|
||||
}
|
||||
return h.log
|
||||
}
|
||||
|
||||
func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo *api.ReqInfo, err error, additional ...zap.Field) {
|
||||
code := api.WriteErrorResponse(w, reqInfo, transformToS3Error(err))
|
||||
func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo *middleware.ReqInfo, err error, additional ...zap.Field) {
|
||||
code := middleware.WriteErrorResponse(w, reqInfo, transformToS3Error(err))
|
||||
fields := []zap.Field{
|
||||
zap.Int("status", code),
|
||||
zap.String("request_id", reqInfo.RequestID),
|
||||
|
@ -38,8 +39,8 @@ func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo
|
|||
h.log.Error("request failed", fields...) // consider using h.reqLogger (it requires accept context.Context or http.Request)
|
||||
}
|
||||
|
||||
func (h *handler) logAndSendErrorNoHeader(w http.ResponseWriter, logText string, reqInfo *api.ReqInfo, err error, additional ...zap.Field) {
|
||||
api.WriteErrorResponseNoHeader(w, reqInfo, transformToS3Error(err))
|
||||
func (h *handler) logAndSendErrorNoHeader(w http.ResponseWriter, logText string, reqInfo *middleware.ReqInfo, err error, additional ...zap.Field) {
|
||||
middleware.WriteErrorResponseNoHeader(w, reqInfo, transformToS3Error(err))
|
||||
fields := []zap.Field{
|
||||
zap.String("request_id", reqInfo.RequestID),
|
||||
zap.String("method", reqInfo.API),
|
||||
|
|
|
@ -4,14 +4,14 @@ import (
|
|||
"encoding/xml"
|
||||
"net/http"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
)
|
||||
|
||||
func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
configuration := new(VersioningConfiguration)
|
||||
if err := xml.NewDecoder(r.Body).Decode(configuration); err != nil {
|
||||
|
@ -57,7 +57,7 @@ func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Requ
|
|||
|
||||
// GetBucketVersioningHandler implements bucket versioning getter handler.
|
||||
func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
reqInfo := middleware.GetReqInfo(r.Context())
|
||||
|
||||
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
|
@ -71,7 +71,7 @@ func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Requ
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, formVersioningConfiguration(settings)); err != nil {
|
||||
if err = middleware.EncodeToResponse(w, formVersioningConfiguration(settings)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
|
71
api/host_bucket_router.go
Normal file
71
api/host_bucket_router.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type HostBucketRouter struct {
|
||||
routes map[string]chi.Router
|
||||
bktParam string
|
||||
defaultRouter chi.Router
|
||||
}
|
||||
|
||||
func NewHostBucketRouter(bktParam string) HostBucketRouter {
|
||||
return HostBucketRouter{
|
||||
routes: make(map[string]chi.Router),
|
||||
bktParam: bktParam,
|
||||
}
|
||||
}
|
||||
|
||||
func (hr *HostBucketRouter) Default(router chi.Router) {
|
||||
hr.defaultRouter = router
|
||||
}
|
||||
|
||||
func (hr HostBucketRouter) Map(host string, h chi.Router) {
|
||||
hr.routes[strings.ToLower(host)] = h
|
||||
}
|
||||
|
||||
func (hr HostBucketRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
bucket, domain := getBucketDomain(getHost(r))
|
||||
router, ok := hr.routes[strings.ToLower(domain)]
|
||||
if !ok {
|
||||
router = hr.defaultRouter
|
||||
if router == nil {
|
||||
http.Error(w, http.StatusText(404), 404)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if rctx := chi.RouteContext(r.Context()); rctx != nil && bucket != "" {
|
||||
rctx.URLParams.Add(hr.bktParam, bucket)
|
||||
}
|
||||
|
||||
router.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func getBucketDomain(host string) (bucket string, domain string) {
|
||||
parts := strings.Split(host, ".")
|
||||
if len(parts) > 1 {
|
||||
return parts[0], strings.Join(parts[1:], ".")
|
||||
}
|
||||
return "", host
|
||||
}
|
||||
|
||||
// getHost tries its best to return the request host.
|
||||
// According to section 14.23 of RFC 2616 the Host header
|
||||
// can include the port number if the default value of 80 is not used.
|
||||
func getHost(r *http.Request) string {
|
||||
host := r.Host
|
||||
if r.URL.IsAbs() {
|
||||
host = r.URL.Host
|
||||
}
|
||||
|
||||
if i := strings.Index(host, ":"); i != -1 {
|
||||
host = host[:i]
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
|
@ -11,7 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
|
@ -331,7 +331,7 @@ func (t *TestFrostFS) isPublicRead(cnrID cid.ID) bool {
|
|||
}
|
||||
|
||||
func getOwner(ctx context.Context) user.ID {
|
||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
if bd, ok := ctx.Value(middleware.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
return bearer.ResolveIssuer(*bd.Gate.BearerToken)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
|
@ -303,13 +304,13 @@ func (n *layer) IsNotificationEnabled() bool {
|
|||
|
||||
// IsAuthenticatedRequest checks if access box exists in the current request.
|
||||
func IsAuthenticatedRequest(ctx context.Context) bool {
|
||||
_, ok := ctx.Value(api.BoxData).(*accessbox.Box)
|
||||
_, ok := ctx.Value(middleware.BoxData).(*accessbox.Box)
|
||||
return ok
|
||||
}
|
||||
|
||||
// TimeNow returns client time from request or time.Now().
|
||||
func TimeNow(ctx context.Context) time.Time {
|
||||
if now, ok := ctx.Value(api.ClientTime).(time.Time); ok {
|
||||
if now, ok := ctx.Value(middleware.ClientTime).(time.Time); ok {
|
||||
return now
|
||||
}
|
||||
|
||||
|
@ -318,7 +319,7 @@ func TimeNow(ctx context.Context) time.Time {
|
|||
|
||||
// Owner returns owner id from BearerToken (context) or from client owner.
|
||||
func (n *layer) Owner(ctx context.Context) user.ID {
|
||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
if bd, ok := ctx.Value(middleware.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
return bearer.ResolveIssuer(*bd.Gate.BearerToken)
|
||||
}
|
||||
|
||||
|
@ -329,7 +330,7 @@ func (n *layer) Owner(ctx context.Context) user.ID {
|
|||
}
|
||||
|
||||
func (n *layer) reqLogger(ctx context.Context) *zap.Logger {
|
||||
reqLogger := api.GetReqLog(ctx)
|
||||
reqLogger := middleware.GetReqLog(ctx)
|
||||
if reqLogger != nil {
|
||||
return reqLogger
|
||||
}
|
||||
|
@ -337,7 +338,7 @@ func (n *layer) reqLogger(ctx context.Context) *zap.Logger {
|
|||
}
|
||||
|
||||
func (n *layer) prepareAuthParameters(ctx context.Context, prm *PrmAuth, bktOwner user.ID) {
|
||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
if bd, ok := ctx.Value(middleware.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
||||
if bd.Gate.BearerToken.Impersonate() || bktOwner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) {
|
||||
prm.BearerToken = bd.Gate.BearerToken
|
||||
return
|
||||
|
|
|
@ -7,13 +7,13 @@ import (
|
|||
errorsStd "errors"
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type PutBucketNotificationConfigurationParams struct {
|
||||
RequestInfo *api.ReqInfo
|
||||
RequestInfo *middleware.ReqInfo
|
||||
BktInfo *data.BucketInfo
|
||||
Configuration *data.NotificationConfiguration
|
||||
CopiesNumbers []uint32
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
)
|
||||
|
@ -141,7 +141,7 @@ func NameFromString(name string) (string, string) {
|
|||
// GetBoxData extracts accessbox.Box from context.
|
||||
func GetBoxData(ctx context.Context) (*accessbox.Box, error) {
|
||||
var boxData *accessbox.Box
|
||||
data, ok := ctx.Value(api.BoxData).(*accessbox.Box)
|
||||
data, ok := ctx.Value(middleware.BoxData).(*accessbox.Box)
|
||||
if !ok || data == nil {
|
||||
return nil, fmt.Errorf("couldn't get box data from context")
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
|
@ -140,7 +140,7 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext {
|
|||
bearerToken := bearertest.Token()
|
||||
require.NoError(t, bearerToken.Sign(key.PrivateKey))
|
||||
|
||||
ctx := context.WithValue(context.Background(), api.BoxData, &accessbox.Box{
|
||||
ctx := context.WithValue(context.Background(), middleware.BoxData, &accessbox.Box{
|
||||
Gate: &accessbox.GateData{
|
||||
BearerToken: &bearerToken,
|
||||
GateKey: key.PublicKey(),
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
// MaxClients provides HTTP handler wrapper with the client limit.
|
||||
MaxClients interface {
|
||||
Handle(http.HandlerFunc) http.HandlerFunc
|
||||
}
|
||||
|
||||
maxClients struct {
|
||||
pool chan struct{}
|
||||
timeout time.Duration
|
||||
}
|
||||
)
|
||||
|
||||
const defaultRequestDeadline = time.Second * 30
|
||||
|
||||
// NewMaxClientsMiddleware returns MaxClients interface with handler wrapper based on
|
||||
// the provided count and the timeout limits.
|
||||
func NewMaxClientsMiddleware(count int, timeout time.Duration) MaxClients {
|
||||
if timeout <= 0 {
|
||||
timeout = defaultRequestDeadline
|
||||
}
|
||||
|
||||
return &maxClients{
|
||||
pool: make(chan struct{}, count),
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Handler wraps HTTP handler function with logic limiting access to it.
|
||||
func (m *maxClients) Handle(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if m.pool == nil {
|
||||
f.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
deadline := time.NewTimer(m.timeout)
|
||||
defer deadline.Stop()
|
||||
|
||||
select {
|
||||
case m.pool <- struct{}{}:
|
||||
defer func() { <-m.pool }()
|
||||
f.ServeHTTP(w, r)
|
||||
case <-deadline.C:
|
||||
// Send a http timeout message
|
||||
WriteErrorResponse(w, GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrOperationTimedOut))
|
||||
return
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"github.com/gorilla/mux"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -22,8 +21,7 @@ var BoxData = KeyWrapper("__context_box_key")
|
|||
// ClientTime is an ID used to store client time.Time in a context.
|
||||
var ClientTime = KeyWrapper("__context_client_time")
|
||||
|
||||
// AuthMiddleware adds user authentication via center to router using log for logging.
|
||||
func AuthMiddleware(log *zap.Logger, center auth.Center) mux.MiddlewareFunc {
|
||||
func Auth(center auth.Center, log *zap.Logger) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
@ -51,11 +49,3 @@ func AuthMiddleware(log *zap.Logger, center auth.Center) mux.MiddlewareFunc {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func reqLogOrDefault(ctx context.Context, log *zap.Logger) *zap.Logger {
|
||||
reqLog := GetReqLog(ctx)
|
||||
if reqLog != nil {
|
||||
return reqLog
|
||||
}
|
||||
return log
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -9,12 +9,102 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func RequestTypeFromAPI(api string) metrics.RequestType {
|
||||
type (
|
||||
UsersStat interface {
|
||||
Update(user, bucket, cnrID string, reqType int, in, out uint64)
|
||||
}
|
||||
|
||||
readCounter struct {
|
||||
io.ReadCloser
|
||||
countBytes uint64
|
||||
}
|
||||
|
||||
writeCounter struct {
|
||||
http.ResponseWriter
|
||||
countBytes uint64
|
||||
}
|
||||
|
||||
responseWrapper struct {
|
||||
sync.Once
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// BucketResolveFunc is a func to resolve bucket info by name.
|
||||
BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error)
|
||||
|
||||
// cidResolveFunc is a func to resolve CID in Stats handler.
|
||||
cidResolveFunc func(ctx context.Context, reqInfo *ReqInfo) (cnrID string)
|
||||
)
|
||||
|
||||
const systemPath = "/system"
|
||||
|
||||
// Metrics wraps http handler for api with basic statistics collection.
|
||||
func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
// Stats is a handler that update metrics.
|
||||
func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := GetReqInfo(r.Context())
|
||||
|
||||
appMetrics.Statistic().CurrentS3RequestsInc(reqInfo.API)
|
||||
defer appMetrics.Statistic().CurrentS3RequestsDec(reqInfo.API)
|
||||
|
||||
in := &readCounter{ReadCloser: r.Body}
|
||||
out := &writeCounter{ResponseWriter: w}
|
||||
|
||||
r.Body = in
|
||||
|
||||
statsWriter := &responseWrapper{
|
||||
ResponseWriter: out,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
|
||||
f(statsWriter, r)
|
||||
|
||||
// Time duration in secs since the call started.
|
||||
// We don't need to do nanosecond precision here
|
||||
// simply for the fact that it is not human-readable.
|
||||
durationSecs := time.Since(statsWriter.startTime).Seconds()
|
||||
|
||||
user := resolveUser(r.Context())
|
||||
cnrID := resolveCID(r.Context(), reqInfo)
|
||||
appMetrics.Update(user, reqInfo.BucketName, cnrID, requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes)
|
||||
|
||||
code := statsWriter.statusCode
|
||||
// A successful request has a 2xx response code
|
||||
successReq := code >= http.StatusOK && code < http.StatusMultipleChoices
|
||||
if !strings.HasSuffix(r.URL.Path, systemPath) {
|
||||
appMetrics.Statistic().TotalS3RequestsInc(reqInfo.API)
|
||||
if !successReq && code != 0 {
|
||||
appMetrics.Statistic().TotalS3ErrorsInc(reqInfo.API)
|
||||
}
|
||||
}
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
// Increment the prometheus http request response histogram with appropriate label
|
||||
appMetrics.Statistic().RequestDurationsUpdate(reqInfo.API, durationSecs)
|
||||
}
|
||||
|
||||
appMetrics.Statistic().TotalInputBytesAdd(in.countBytes)
|
||||
appMetrics.Statistic().TotalOutputBytesAdd(out.countBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func requestTypeFromAPI(api string) metrics.RequestType {
|
||||
switch api {
|
||||
case "Options", "HeadObject", "HeadBucket":
|
||||
return metrics.HEADRequest
|
||||
|
@ -43,83 +133,20 @@ func RequestTypeFromAPI(api string) metrics.RequestType {
|
|||
}
|
||||
}
|
||||
|
||||
type (
|
||||
UsersStat interface {
|
||||
Update(user, bucket, cnrID string, reqType int, in, out uint64)
|
||||
}
|
||||
|
||||
readCounter struct {
|
||||
io.ReadCloser
|
||||
countBytes uint64
|
||||
}
|
||||
|
||||
writeCounter struct {
|
||||
http.ResponseWriter
|
||||
countBytes uint64
|
||||
}
|
||||
|
||||
responseWrapper struct {
|
||||
sync.Once
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
startTime time.Time
|
||||
}
|
||||
)
|
||||
|
||||
const systemPath = "/system"
|
||||
|
||||
//var apiStatMetrics = metrics.newApiStatMetrics()
|
||||
|
||||
// CIDResolveFunc is a func to resolve CID in Stats handler.
|
||||
type CIDResolveFunc func(ctx context.Context, reqInfo *ReqInfo) (cnrID string)
|
||||
|
||||
// Stats is a handler that update metrics.
|
||||
func Stats(f http.HandlerFunc, resolveCID CIDResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := GetReqInfo(r.Context())
|
||||
|
||||
appMetrics.Statistic().CurrentS3RequestsInc(reqInfo.API)
|
||||
defer appMetrics.Statistic().CurrentS3RequestsDec(reqInfo.API)
|
||||
|
||||
in := &readCounter{ReadCloser: r.Body}
|
||||
out := &writeCounter{ResponseWriter: w}
|
||||
|
||||
r.Body = in
|
||||
|
||||
statsWriter := &responseWrapper{
|
||||
ResponseWriter: out,
|
||||
startTime: time.Now(),
|
||||
// resolveCID forms CIDResolveFunc using BucketResolveFunc.
|
||||
func resolveCID(log *zap.Logger, resolveBucket BucketResolveFunc) cidResolveFunc {
|
||||
return func(ctx context.Context, reqInfo *ReqInfo) (cnrID string) {
|
||||
if reqInfo.BucketName == "" || reqInfo.API == "CreateBucket" || reqInfo.API == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
f(statsWriter, r)
|
||||
|
||||
// Time duration in secs since the call started.
|
||||
// We don't need to do nanosecond precision here
|
||||
// simply for the fact that it is not human-readable.
|
||||
durationSecs := time.Since(statsWriter.startTime).Seconds()
|
||||
|
||||
user := resolveUser(r.Context())
|
||||
cnrID := resolveCID(r.Context(), reqInfo)
|
||||
appMetrics.Update(user, reqInfo.BucketName, cnrID, RequestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes)
|
||||
|
||||
code := statsWriter.statusCode
|
||||
// A successful request has a 2xx response code
|
||||
successReq := code >= http.StatusOK && code < http.StatusMultipleChoices
|
||||
if !strings.HasSuffix(r.URL.Path, systemPath) {
|
||||
appMetrics.Statistic().TotalS3RequestsInc(reqInfo.API)
|
||||
if !successReq && code != 0 {
|
||||
appMetrics.Statistic().TotalS3ErrorsInc(reqInfo.API)
|
||||
}
|
||||
bktInfo, err := resolveBucket(ctx, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
reqLogOrDefault(ctx, log).Debug("failed to resolve CID", zap.Error(err))
|
||||
return ""
|
||||
}
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
// Increment the prometheus http request response histogram with appropriate label
|
||||
appMetrics.Statistic().RequestDurationsUpdate(reqInfo.API, durationSecs)
|
||||
}
|
||||
|
||||
appMetrics.Statistic().TotalInputBytesAdd(in.countBytes)
|
||||
appMetrics.Statistic().TotalOutputBytesAdd(out.countBytes)
|
||||
return bktInfo.CID.EncodeToString()
|
||||
}
|
||||
}
|
||||
|
27
api/middleware/middleware.go
Normal file
27
api/middleware/middleware.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Func func(h http.Handler) http.Handler
|
||||
|
||||
func WrapHandler(handler http.HandlerFunc) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handler(w, r)
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func reqLogOrDefault(ctx context.Context, log *zap.Logger) *zap.Logger {
|
||||
reqLog := GetReqLog(ctx)
|
||||
if reqLog != nil {
|
||||
return reqLog
|
||||
}
|
||||
return log
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -9,8 +9,10 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -41,23 +43,30 @@ type (
|
|||
Object string
|
||||
Method string
|
||||
}
|
||||
)
|
||||
|
||||
// Key used for custom key/value in context.
|
||||
type contextKeyType string
|
||||
// Key used for custom key/value in context.
|
||||
contextKeyType string
|
||||
)
|
||||
|
||||
const (
|
||||
ctxRequestInfo = contextKeyType("FrostFS-S3-GW")
|
||||
ctxRequestLogger = contextKeyType("FrostFS-S3-GW-Logger")
|
||||
)
|
||||
|
||||
const HdrAmzRequestID = "x-amz-request-id"
|
||||
|
||||
const (
|
||||
BucketURLPrm = "bucket"
|
||||
ObjectURLPrm = "object"
|
||||
)
|
||||
|
||||
var deploymentID = uuid.Must(uuid.NewRandom())
|
||||
|
||||
var (
|
||||
// De-facto standard header keys.
|
||||
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
||||
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
||||
)
|
||||
|
||||
var (
|
||||
// RFC7239 defines a new "Forwarded: " header designed to replace the
|
||||
// existing use of X-Forwarded-* headers.
|
||||
// e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43.
|
||||
|
@ -67,68 +76,6 @@ var (
|
|||
forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|, )]+)(.*)`)
|
||||
)
|
||||
|
||||
// GetSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
|
||||
// Forwarded headers (in that order), falls back to r.RemoteAddr when everything
|
||||
// else fails.
|
||||
func GetSourceIP(r *http.Request) string {
|
||||
var addr string
|
||||
|
||||
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
||||
// Only grabs the first (client) address. Note that '192.168.0.1,
|
||||
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
||||
// the first one may represent forwarding proxies earlier in the chain.
|
||||
s := strings.Index(fwd, ", ")
|
||||
if s == -1 {
|
||||
s = len(fwd)
|
||||
}
|
||||
addr = fwd[:s]
|
||||
} else if fwd := r.Header.Get(xRealIP); fwd != "" {
|
||||
// X-Real-IP should only contain one IP address (the client making the
|
||||
// request).
|
||||
addr = fwd
|
||||
} else if fwd := r.Header.Get(forwarded); fwd != "" {
|
||||
// match should contain at least two elements if the protocol was
|
||||
// specified in the Forwarded header. The first element will always be
|
||||
// the 'for=' capture, which we ignore. In the case of multiple IP
|
||||
// addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only
|
||||
// extract the first, which should be the client IP.
|
||||
if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
|
||||
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
|
||||
// these quotes.
|
||||
addr = strings.Trim(match[1], `"`)
|
||||
}
|
||||
}
|
||||
|
||||
if addr != "" {
|
||||
return addr
|
||||
}
|
||||
|
||||
// Default to remote address if headers not set.
|
||||
addr, _, _ = net.SplitHostPort(r.RemoteAddr)
|
||||
return addr
|
||||
}
|
||||
|
||||
func prepareReqInfo(w http.ResponseWriter, r *http.Request) *ReqInfo {
|
||||
vars := mux.Vars(r)
|
||||
bucket := vars["bucket"]
|
||||
object, err := url.PathUnescape(vars["object"])
|
||||
if err != nil {
|
||||
object = vars["object"]
|
||||
}
|
||||
prefix, err := url.QueryUnescape(vars["prefix"])
|
||||
if err != nil {
|
||||
prefix = vars["prefix"]
|
||||
}
|
||||
if prefix != "" {
|
||||
object = prefix
|
||||
}
|
||||
return NewReqInfo(w, r, ObjectRequest{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
Method: mux.CurrentRoute(r).GetName(),
|
||||
})
|
||||
}
|
||||
|
||||
// NewReqInfo returns new ReqInfo based on parameters.
|
||||
func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqInfo {
|
||||
return &ReqInfo{
|
||||
|
@ -136,7 +83,7 @@ func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqI
|
|||
BucketName: req.Bucket,
|
||||
ObjectName: req.Object,
|
||||
UserAgent: r.UserAgent(),
|
||||
RemoteHost: GetSourceIP(r),
|
||||
RemoteHost: getSourceIP(r),
|
||||
RequestID: GetRequestID(w),
|
||||
DeploymentID: deploymentID.String(),
|
||||
URL: r.URL,
|
||||
|
@ -187,6 +134,18 @@ func (r *ReqInfo) GetTags() []KeyVal {
|
|||
return append([]KeyVal(nil), r.tags...)
|
||||
}
|
||||
|
||||
// GetRequestID returns the request ID from the response writer or the context.
|
||||
func GetRequestID(v interface{}) string {
|
||||
switch t := v.(type) {
|
||||
case context.Context:
|
||||
return GetReqInfo(t).RequestID
|
||||
case http.ResponseWriter:
|
||||
return t.Header().Get(HdrAmzRequestID)
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
// SetReqInfo sets ReqInfo in the context.
|
||||
func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context {
|
||||
if ctx == nil {
|
||||
|
@ -224,3 +183,124 @@ func GetReqLog(ctx context.Context) *zap.Logger {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Request(log *zap.Logger) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// generate random UUIDv4
|
||||
id, _ := uuid.NewRandom()
|
||||
|
||||
// set request id into response header
|
||||
// also we have to set request id here
|
||||
// to be able to get it in NewReqInfo
|
||||
w.Header().Set(HdrAmzRequestID, id.String())
|
||||
|
||||
// set request info into context
|
||||
// bucket name and object will be set in reqInfo later (limitation of go-chi)
|
||||
reqInfo := NewReqInfo(w, r, ObjectRequest{})
|
||||
r = r.WithContext(SetReqInfo(r.Context(), reqInfo))
|
||||
|
||||
// set request id into gRPC meta header
|
||||
r = r.WithContext(metadata.AppendToOutgoingContext(
|
||||
r.Context(), HdrAmzRequestID, reqInfo.RequestID,
|
||||
))
|
||||
|
||||
reqLogger := log.With(zap.String("request_id", reqInfo.RequestID))
|
||||
r = r.WithContext(SetReqLogger(r.Context(), reqLogger))
|
||||
|
||||
reqLogger.Info("request start", zap.String("host", r.Host),
|
||||
zap.String("remote_host", reqInfo.RemoteHost))
|
||||
|
||||
// continue execution
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AddBucketName adds bucket name to ReqInfo from context.
|
||||
func AddBucketName(l *zap.Logger) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
reqInfo := GetReqInfo(ctx)
|
||||
reqInfo.BucketName = chi.URLParam(r, BucketURLPrm)
|
||||
|
||||
reqLogger := reqLogOrDefault(ctx, l)
|
||||
r = r.WithContext(SetReqLogger(ctx, reqLogger.With(zap.String("bucket", reqInfo.BucketName))))
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AddObjectName adds objects name to ReqInfo from context.
|
||||
func AddObjectName(l *zap.Logger) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
obj := chi.URLParam(r, ObjectURLPrm)
|
||||
object, err := url.PathUnescape(obj)
|
||||
if err != nil {
|
||||
object = obj
|
||||
}
|
||||
prefix, err := url.QueryUnescape(chi.URLParam(r, "prefix"))
|
||||
if err != nil {
|
||||
prefix = chi.URLParam(r, "prefix")
|
||||
}
|
||||
if prefix != "" {
|
||||
object = prefix
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
reqInfo := GetReqInfo(ctx)
|
||||
reqInfo.ObjectName = object
|
||||
|
||||
reqLogger := reqLogOrDefault(ctx, l)
|
||||
r = r.WithContext(SetReqLogger(ctx, reqLogger.With(zap.String("object", reqInfo.ObjectName))))
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
|
||||
// Forwarded headers (in that order), falls back to r.RemoteAddr when everything
|
||||
// else fails.
|
||||
func getSourceIP(r *http.Request) string {
|
||||
var addr string
|
||||
|
||||
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
||||
// Only grabs the first (client) address. Note that '192.168.0.1,
|
||||
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
||||
// the first one may represent forwarding proxies earlier in the chain.
|
||||
s := strings.Index(fwd, ", ")
|
||||
if s == -1 {
|
||||
s = len(fwd)
|
||||
}
|
||||
addr = fwd[:s]
|
||||
} else if fwd := r.Header.Get(xRealIP); fwd != "" {
|
||||
// X-Real-IP should only contain one IP address (the client making the
|
||||
// request).
|
||||
addr = fwd
|
||||
} else if fwd := r.Header.Get(forwarded); fwd != "" {
|
||||
// match should contain at least two elements if the protocol was
|
||||
// specified in the Forwarded header. The first element will always be
|
||||
// the 'for=' capture, which we ignore. In the case of multiple IP
|
||||
// addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only
|
||||
// extract the first, which should be the client IP.
|
||||
if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
|
||||
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
|
||||
// these quotes.
|
||||
addr = strings.Trim(match[1], `"`)
|
||||
}
|
||||
}
|
||||
|
||||
if addr != "" {
|
||||
return addr
|
||||
}
|
||||
|
||||
// Default to remote address if headers not set.
|
||||
addr, _, _ = net.SplitHostPort(r.RemoteAddr)
|
||||
return addr
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -6,10 +6,11 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -34,19 +35,26 @@ type (
|
|||
// Underlying HTTP status code for the returned error.
|
||||
StatusCode int `xml:"-" json:"-"`
|
||||
}
|
||||
|
||||
// mimeType represents various MIME types used in API responses.
|
||||
mimeType string
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// MimeNone means no response type.
|
||||
MimeNone mimeType = ""
|
||||
|
||||
// MimeXML means response type is XML.
|
||||
MimeXML mimeType = "application/xml"
|
||||
|
||||
hdrServerInfo = "Server"
|
||||
hdrAcceptRanges = "Accept-Ranges"
|
||||
hdrContentType = "Content-Type"
|
||||
hdrContentLength = "Content-Length"
|
||||
hdrRetryAfter = "Retry-After"
|
||||
|
||||
hdrAmzCopySource = "X-Amz-Copy-Source"
|
||||
|
||||
// Response request id.
|
||||
hdrAmzRequestID = "x-amz-request-id"
|
||||
|
||||
// hdrSSE is the general AWS SSE HTTP header key.
|
||||
hdrSSE = "X-Amz-Server-Side-Encryption"
|
||||
|
@ -61,8 +69,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
deploymentID, _ = uuid.NewRandom()
|
||||
|
||||
xmlHeader = []byte(xml.Header)
|
||||
)
|
||||
|
||||
|
@ -140,16 +146,6 @@ func WriteErrorResponseNoHeader(w http.ResponseWriter, reqInfo *ReqInfo, err err
|
|||
WriteResponseBody(w, encodedErrorResponse)
|
||||
}
|
||||
|
||||
// If none of the http routes match respond with appropriate errors.
|
||||
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
|
||||
WriteErrorResponse(w, GetReqInfo(r.Context()), errors.Error{
|
||||
Code: "UnknownAPIRequest",
|
||||
Description: desc,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
}
|
||||
|
||||
// Write http common headers.
|
||||
func setCommonHeaders(w http.ResponseWriter) {
|
||||
w.Header().Set(hdrServerInfo, version.Server)
|
||||
|
@ -280,3 +276,56 @@ func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse {
|
|||
HostID: info.DeploymentID,
|
||||
}
|
||||
}
|
||||
|
||||
type logResponseWriter struct {
|
||||
sync.Once
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (lrw *logResponseWriter) WriteHeader(code int) {
|
||||
lrw.Do(func() {
|
||||
lrw.statusCode = code
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
})
|
||||
}
|
||||
|
||||
func (lrw *logResponseWriter) Flush() {
|
||||
if f, ok := lrw.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func LogSuccessResponse(l *zap.Logger) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
lw := &logResponseWriter{ResponseWriter: w}
|
||||
|
||||
// here reqInfo doesn't contain bucket name and object name
|
||||
|
||||
// pass execution:
|
||||
h.ServeHTTP(lw, r)
|
||||
|
||||
// here reqInfo contains bucket name and object name because of
|
||||
// addBucketName and addObjectName middlewares
|
||||
|
||||
// Ignore >400 status codes
|
||||
if lw.statusCode >= http.StatusBadRequest {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
reqLogger := reqLogOrDefault(ctx, l)
|
||||
reqInfo := GetReqInfo(ctx)
|
||||
|
||||
reqLogger.Info("request end",
|
||||
zap.String("method", reqInfo.API),
|
||||
zap.String("bucket", reqInfo.BucketName),
|
||||
zap.String("object", reqInfo.ObjectName),
|
||||
zap.Int("status", lw.statusCode),
|
||||
zap.String("description", http.StatusText(lw.statusCode)),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,15 +6,14 @@ import (
|
|||
"sync"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||
"github.com/gorilla/mux"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// TracingMiddleware adds tracing support for requests.
|
||||
// Tracing adds tracing support for requests.
|
||||
// Must be placed after prepareRequest middleware.
|
||||
func TracingMiddleware() mux.MiddlewareFunc {
|
||||
func Tracing() Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
appCtx, span := StartHTTPServerSpan(r, "REQUEST S3")
|
795
api/router.go
795
api/router.go
|
@ -2,16 +2,17 @@ package api
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
s3middleware "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -86,185 +87,64 @@ type (
|
|||
|
||||
ResolveBucket(ctx context.Context, bucket string) (*data.BucketInfo, error)
|
||||
}
|
||||
|
||||
// mimeType represents various MIME types used in API responses.
|
||||
mimeType string
|
||||
|
||||
logResponseWriter struct {
|
||||
sync.Once
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// SlashSeparator -- slash separator.
|
||||
SlashSeparator = "/"
|
||||
func AttachChi(api *chi.Mux, domains []string, throttle middleware.ThrottleOpts, h Handler, center auth.Center, log *zap.Logger, appMetrics *metrics.AppMetrics) {
|
||||
api.Use(
|
||||
middleware.CleanPath,
|
||||
s3middleware.Request(log),
|
||||
middleware.ThrottleWithOpts(throttle),
|
||||
middleware.Recoverer,
|
||||
s3middleware.Tracing(),
|
||||
s3middleware.Metrics(log, h.ResolveBucket, appMetrics),
|
||||
s3middleware.LogSuccessResponse(log),
|
||||
s3middleware.Auth(center, log),
|
||||
)
|
||||
|
||||
// MimeNone means no response type.
|
||||
MimeNone mimeType = ""
|
||||
defaultRouter := chi.NewRouter()
|
||||
defaultRouter.Mount(fmt.Sprintf("/{%s}", s3middleware.BucketURLPrm), bucketRouter(h, log))
|
||||
defaultRouter.Get("/", named("ListBuckets", h.ListBucketsHandler))
|
||||
|
||||
// MimeXML means response type is XML.
|
||||
MimeXML mimeType = "application/xml"
|
||||
)
|
||||
hr := NewHostBucketRouter("bucket")
|
||||
hr.Default(defaultRouter)
|
||||
for _, domain := range domains {
|
||||
hr.Map(domain, bucketRouter(h, log))
|
||||
}
|
||||
api.Mount("/", hr)
|
||||
|
||||
var _ = logSuccessResponse
|
||||
|
||||
func (lrw *logResponseWriter) WriteHeader(code int) {
|
||||
lrw.Do(func() {
|
||||
lrw.statusCode = code
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
})
|
||||
attachErrorHandler(api, h, center, log, appMetrics)
|
||||
}
|
||||
|
||||
func (lrw *logResponseWriter) Flush() {
|
||||
if f, ok := lrw.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
func named(name string, handlerFunc http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := s3middleware.GetReqInfo(r.Context())
|
||||
reqInfo.API = name
|
||||
handlerFunc.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareRequest(log *zap.Logger) mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// generate random UUIDv4
|
||||
id, _ := uuid.NewRandom()
|
||||
|
||||
// set request id into response header
|
||||
// also we have to set request id here
|
||||
// to be able to get it in prepareReqInfo
|
||||
w.Header().Set(hdrAmzRequestID, id.String())
|
||||
|
||||
// set request info into context
|
||||
reqInfo := prepareReqInfo(w, r)
|
||||
r = r.WithContext(SetReqInfo(r.Context(), reqInfo))
|
||||
|
||||
// set request id into gRPC meta header
|
||||
r = r.WithContext(metadata.AppendToOutgoingContext(
|
||||
r.Context(), hdrAmzRequestID, reqInfo.RequestID,
|
||||
))
|
||||
|
||||
// set request scoped child logger into context
|
||||
additionalFields := []zap.Field{zap.String("request_id", reqInfo.RequestID),
|
||||
zap.String("method", reqInfo.API), zap.String("bucket", reqInfo.BucketName)}
|
||||
|
||||
if isObjectRequest(reqInfo) {
|
||||
additionalFields = append(additionalFields, zap.String("object", reqInfo.ObjectName))
|
||||
}
|
||||
reqLogger := log.With(additionalFields...)
|
||||
|
||||
r = r.WithContext(SetReqLogger(r.Context(), reqLogger))
|
||||
|
||||
reqLogger.Info("request start", zap.String("host", r.Host),
|
||||
zap.String("remote_host", reqInfo.RemoteHost))
|
||||
|
||||
// continue execution
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var objectMethods = []string{
|
||||
"HeadObject", "GetObject", "DeleteObject", "PutObject", "PostObject", "CopyObject",
|
||||
"CreateMultipartUpload", "UploadPartCopy", "UploadPart", "ListObjectParts",
|
||||
"CompleteMultipartUpload", "AbortMultipartUpload",
|
||||
"PutObjectACL", "GetObjectACL",
|
||||
"PutObjectTagging", "GetObjectTagging", "DeleteObjectTagging",
|
||||
"PutObjectRetention", "GetObjectRetention", "PutObjectLegalHold", "getobjectlegalhold",
|
||||
"SelectObjectContent", "GetObjectAttributes",
|
||||
}
|
||||
|
||||
func isObjectRequest(info *ReqInfo) bool {
|
||||
for _, method := range objectMethods {
|
||||
if info.API == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func appendCORS(handler Handler) mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handler.AppendCORSHeaders(w, r)
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BucketResolveFunc is a func to resolve bucket info by name.
|
||||
type BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error)
|
||||
|
||||
// metricsMiddleware wraps http handler for api with basic statistics collection.
|
||||
func metricsMiddleware(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics) mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return Stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
// resolveCID forms CIDResolveFunc using BucketResolveFunc.
|
||||
func resolveCID(log *zap.Logger, resolveBucket BucketResolveFunc) CIDResolveFunc {
|
||||
return func(ctx context.Context, reqInfo *ReqInfo) (cnrID string) {
|
||||
if reqInfo.BucketName == "" || reqInfo.API == "CreateBucket" || reqInfo.API == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
bktInfo, err := resolveBucket(ctx, reqInfo.BucketName)
|
||||
if err != nil {
|
||||
reqLogOrDefault(ctx, log).Debug("failed to resolve CID", zap.Error(err))
|
||||
return ""
|
||||
}
|
||||
|
||||
return bktInfo.CID.EncodeToString()
|
||||
}
|
||||
}
|
||||
|
||||
func logSuccessResponse(l *zap.Logger) mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
lw := &logResponseWriter{ResponseWriter: w}
|
||||
|
||||
reqLogger := reqLogOrDefault(r.Context(), l)
|
||||
|
||||
// pass execution:
|
||||
h.ServeHTTP(lw, r)
|
||||
|
||||
// Ignore >400 status codes
|
||||
if lw.statusCode >= http.StatusBadRequest {
|
||||
return
|
||||
}
|
||||
|
||||
reqLogger.Info("request end",
|
||||
zap.Int("status", lw.statusCode),
|
||||
zap.String("description", http.StatusText(lw.statusCode)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// GetRequestID returns the request ID from the response writer or the context.
|
||||
func GetRequestID(v interface{}) string {
|
||||
switch t := v.(type) {
|
||||
case context.Context:
|
||||
return GetReqInfo(t).RequestID
|
||||
case http.ResponseWriter:
|
||||
return t.Header().Get(hdrAmzRequestID)
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func setErrorAPI(apiName string, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := SetReqInfo(r.Context(), &ReqInfo{API: apiName})
|
||||
func setErrorAPI(apiName string, h http.Handler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := s3middleware.SetReqInfo(r.Context(), &s3middleware.ReqInfo{API: apiName})
|
||||
h.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the http routes match respond with appropriate errors.
|
||||
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
|
||||
s3middleware.WriteErrorResponse(w, s3middleware.GetReqInfo(r.Context()), errors.Error{
|
||||
Code: "UnknownAPIRequest",
|
||||
Description: desc,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
}
|
||||
|
||||
// attachErrorHandler set NotFoundHandler and MethodNotAllowedHandler for mux.Router.
|
||||
func attachErrorHandler(api *mux.Router, log *zap.Logger, h Handler, center auth.Center, appMetrics *metrics.AppMetrics) {
|
||||
middlewares := []mux.MiddlewareFunc{
|
||||
AuthMiddleware(log, center),
|
||||
metricsMiddleware(log, h.ResolveBucket, appMetrics),
|
||||
// attachErrorHandler set NotFoundHandler and MethodNotAllowedHandler for chi.Router.
|
||||
func attachErrorHandler(api *chi.Mux, h Handler, center auth.Center, log *zap.Logger, appMetrics *metrics.AppMetrics) {
|
||||
middlewares := chi.Middlewares{
|
||||
s3middleware.Auth(center, log),
|
||||
s3middleware.Metrics(log, h.ResolveBucket, appMetrics),
|
||||
}
|
||||
|
||||
var errorHandler http.Handler = http.HandlerFunc(errorResponseHandler)
|
||||
|
@ -273,367 +153,240 @@ func attachErrorHandler(api *mux.Router, log *zap.Logger, h Handler, center auth
|
|||
}
|
||||
|
||||
// If none of the routes match, add default error handler routes
|
||||
api.NotFoundHandler = setErrorAPI("NotFound", errorHandler)
|
||||
api.MethodNotAllowedHandler = setErrorAPI("MethodNotAllowed", errorHandler)
|
||||
api.NotFound(setErrorAPI("NotFound", errorHandler))
|
||||
api.MethodNotAllowed(setErrorAPI("MethodNotAllowed", errorHandler))
|
||||
}
|
||||
|
||||
// Attach adds S3 API handlers from h to r for domains with m client limit using
|
||||
// center authentication and log logger.
|
||||
func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center auth.Center, log *zap.Logger, appMetrics *metrics.AppMetrics) {
|
||||
api := r.PathPrefix(SlashSeparator).Subrouter()
|
||||
|
||||
api.Use(
|
||||
// -- prepare request
|
||||
prepareRequest(log),
|
||||
|
||||
// Attach user authentication for all S3 routes.
|
||||
AuthMiddleware(log, center),
|
||||
|
||||
TracingMiddleware(),
|
||||
|
||||
metricsMiddleware(log, h.ResolveBucket, appMetrics),
|
||||
|
||||
// -- logging error requests
|
||||
logSuccessResponse(log),
|
||||
func bucketRouter(h Handler, log *zap.Logger) chi.Router {
|
||||
bktRouter := chi.NewRouter()
|
||||
bktRouter.Use(
|
||||
s3middleware.AddBucketName(log),
|
||||
s3middleware.WrapHandler(h.AppendCORSHeaders),
|
||||
)
|
||||
|
||||
attachErrorHandler(api, log, h, center, appMetrics)
|
||||
bktRouter.Mount(fmt.Sprintf("/{%s}", s3middleware.ObjectURLPrm), objectRouter(h, log))
|
||||
|
||||
buckets := make([]*mux.Router, 0, len(domains)+1)
|
||||
buckets = append(buckets, api.PathPrefix("/{bucket}").Subrouter())
|
||||
bktRouter.Options("/", h.Preflight)
|
||||
|
||||
for _, domain := range domains {
|
||||
buckets = append(buckets, api.Host("{bucket:.+}."+domain).Subrouter())
|
||||
}
|
||||
bktRouter.Head("/", named("HeadBucket", h.HeadBucketHandler))
|
||||
|
||||
for _, bucket := range buckets {
|
||||
// Object operations
|
||||
// HeadObject
|
||||
bucket.Use(
|
||||
// -- append CORS headers to a response for
|
||||
appendCORS(h),
|
||||
)
|
||||
bucket.Methods(http.MethodOptions).HandlerFunc(
|
||||
m.Handle(h.Preflight)).
|
||||
Name("Options")
|
||||
bucket.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.HeadObjectHandler)).
|
||||
Name("HeadObject")
|
||||
// CopyObjectPart
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(
|
||||
m.Handle(h.UploadPartCopy)).
|
||||
Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").
|
||||
Name("UploadPartCopy")
|
||||
// PutObjectPart
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.UploadPartHandler)).
|
||||
Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").
|
||||
Name("UploadPart")
|
||||
// ListParts
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.ListPartsHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}").
|
||||
Name("ListObjectParts")
|
||||
// CompleteMultipartUpload
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.CompleteMultipartUploadHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}").
|
||||
Name("CompleteMultipartUpload")
|
||||
// CreateMultipartUpload
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.CreateMultipartUploadHandler)).
|
||||
Queries("uploads", "").
|
||||
Name("CreateMultipartUpload")
|
||||
// AbortMultipartUpload
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.AbortMultipartUploadHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}").
|
||||
Name("AbortMultipartUpload")
|
||||
// ListMultipartUploads
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.ListMultipartUploadsHandler)).
|
||||
Queries("uploads", "").
|
||||
Name("ListMultipartUploads")
|
||||
// GetObjectACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectACLHandler)).
|
||||
Queries("acl", "").
|
||||
Name("GetObjectACL")
|
||||
// PutObjectACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.PutObjectACLHandler)).
|
||||
Queries("acl", "").
|
||||
Name("PutObjectACL")
|
||||
// GetObjectTagging
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("GetObjectTagging")
|
||||
// PutObjectTagging
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.PutObjectTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("PutObjectTagging")
|
||||
// DeleteObjectTagging
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.DeleteObjectTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("DeleteObjectTagging")
|
||||
// SelectObjectContent
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.SelectObjectContentHandler)).
|
||||
Queries("select", "").Queries("select-type", "2").
|
||||
Name("SelectObjectContent")
|
||||
// GetObjectRetention
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectRetentionHandler)).
|
||||
Queries("retention", "").
|
||||
Name("GetObjectRetention")
|
||||
// GetObjectLegalHold
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectLegalHoldHandler)).
|
||||
Queries("legal-hold", "").
|
||||
Name("GetObjectLegalHold")
|
||||
// GetObjectAttributes
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectAttributesHandler)).
|
||||
Queries("attributes", "").
|
||||
Name("GetObjectAttributes")
|
||||
// GetObject
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.GetObjectHandler)).
|
||||
Name("GetObject")
|
||||
// CopyObject
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(
|
||||
m.Handle(h.CopyObjectHandler)).
|
||||
Name("CopyObject")
|
||||
// PutObjectRetention
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.PutObjectRetentionHandler)).
|
||||
Queries("retention", "").
|
||||
Name("PutObjectRetention")
|
||||
// PutObjectLegalHold
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.PutObjectLegalHoldHandler)).
|
||||
Queries("legal-hold", "").
|
||||
Name("PutObjectLegalHold")
|
||||
// GET method handlers
|
||||
bktRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodGet, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("upload").
|
||||
Handler(named("ListMultipartUploads", h.ListMultipartUploadsHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("location").
|
||||
Handler(named("GetBucketLocation", h.GetBucketLocationHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("policy").
|
||||
Handler(named("GetBucketPolicy", h.GetBucketPolicyHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("lifecycle").
|
||||
Handler(named("GetBucketLifecycle", h.GetBucketLifecycleHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("encryption").
|
||||
Handler(named("GetBucketEncryption", h.GetBucketEncryptionHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("cors").
|
||||
Handler(named("GetBucketCors", h.GetBucketCorsHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("acl").
|
||||
Handler(named("GetBucketACL", h.GetBucketACLHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("website").
|
||||
Handler(named("GetBucketWebsite", h.GetBucketWebsiteHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("accelerate").
|
||||
Handler(named("GetBucketAccelerate", h.GetBucketAccelerateHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("requestPayment").
|
||||
Handler(named("GetBucketRequestPayment", h.GetBucketRequestPaymentHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("logging").
|
||||
Handler(named("GetBucketLogging", h.GetBucketLoggingHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("replication").
|
||||
Handler(named("GetBucketReplication", h.GetBucketReplicationHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("GetBucketTagging", h.GetBucketTaggingHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("object-lock").
|
||||
Handler(named("GetBucketObjectLockConfig", h.GetBucketObjectLockConfigHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("versioning").
|
||||
Handler(named("GetBucketVersioning", h.GetBucketVersioningHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("notification").
|
||||
Handler(named("GetBucketNotification", h.GetBucketNotificationHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("events").
|
||||
Handler(named("ListenBucketNotification", h.ListenBucketNotificationHandler))).
|
||||
Add(NewFilter().
|
||||
QueriesMatch("list-type", "2", "metadata", "true").
|
||||
Handler(named("ListObjectsV2M", h.ListObjectsV2MHandler))).
|
||||
Add(NewFilter().
|
||||
QueriesMatch("list-type", "2").
|
||||
Handler(named("ListObjectsV2", h.ListObjectsV2Handler))).
|
||||
Add(NewFilter().
|
||||
Queries("versions").
|
||||
Handler(named("ListBucketObjectVersions", h.ListBucketObjectVersionsHandler))).
|
||||
DefaultHandler(named("ListObjectsV1", h.ListObjectsV1Handler)))
|
||||
})
|
||||
|
||||
// PutObject
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.PutObjectHandler)).
|
||||
Name("PutObject")
|
||||
// DeleteObject
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(h.DeleteObjectHandler)).
|
||||
Name("DeleteObject")
|
||||
// PUT method handlers
|
||||
bktRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodPut, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("cors").
|
||||
Handler(named("PutBucketCors", h.PutBucketCorsHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("acl").
|
||||
Handler(named("PutBucketACL", h.PutBucketACLHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("lifecycle").
|
||||
Handler(named("PutBucketLifecycle", h.PutBucketLifecycleHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("encryption").
|
||||
Handler(named("PutBucketEncryption", h.PutBucketEncryptionHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("policy").
|
||||
Handler(named("PutBucketPolicy", h.PutBucketPolicyHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("object-lock").
|
||||
Handler(named("PutBucketObjectLockConfig", h.PutBucketObjectLockConfigHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("PutBucketTagging", h.PutBucketTaggingHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("versioning").
|
||||
Handler(named("PutBucketVersioning", h.PutBucketVersioningHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("notification").
|
||||
Handler(named("PutBucketNotification", h.PutBucketNotificationHandler))).
|
||||
DefaultHandler(named("CreateBucket", h.CreateBucketHandler)))
|
||||
})
|
||||
|
||||
// Bucket operations
|
||||
// GetBucketLocation
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketLocationHandler)).
|
||||
Queries("location", "").
|
||||
Name("GetBucketLocation")
|
||||
// GetBucketPolicy
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketPolicyHandler)).
|
||||
Queries("policy", "").
|
||||
Name("GetBucketPolicy")
|
||||
// GetBucketLifecycle
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "").
|
||||
Name("GetBucketLifecycle")
|
||||
// GetBucketEncryption
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketEncryptionHandler)).
|
||||
Queries("encryption", "").
|
||||
Name("GetBucketEncryption")
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketCorsHandler)).
|
||||
Queries("cors", "").
|
||||
Name("GetBucketCors")
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketCorsHandler)).
|
||||
Queries("cors", "").
|
||||
Name("PutBucketCors")
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketCorsHandler)).
|
||||
Queries("cors", "").
|
||||
Name("DeleteBucketCors")
|
||||
// Dummy Bucket Calls
|
||||
// GetBucketACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketACLHandler)).
|
||||
Queries("acl", "").
|
||||
Name("GetBucketACL")
|
||||
// PutBucketACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketACLHandler)).
|
||||
Queries("acl", "").
|
||||
Name("PutBucketACL")
|
||||
// GetBucketWebsiteHandler -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketWebsiteHandler)).
|
||||
Queries("website", "").
|
||||
Name("GetBucketWebsite")
|
||||
// GetBucketAccelerateHandler -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketAccelerateHandler)).
|
||||
Queries("accelerate", "").
|
||||
Name("GetBucketAccelerate")
|
||||
// GetBucketRequestPaymentHandler -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketRequestPaymentHandler)).
|
||||
Queries("requestPayment", "").
|
||||
Name("GetBucketRequestPayment")
|
||||
// GetBucketLoggingHandler -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketLoggingHandler)).
|
||||
Queries("logging", "").
|
||||
Name("GetBucketLogging")
|
||||
// GetBucketReplicationHandler -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketReplicationHandler)).
|
||||
Queries("replication", "").
|
||||
Name("GetBucketReplication")
|
||||
// GetBucketTaggingHandler
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("GetBucketTagging")
|
||||
// DeleteBucketWebsiteHandler
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketWebsiteHandler)).
|
||||
Queries("website", "").
|
||||
Name("DeleteBucketWebsite")
|
||||
// DeleteBucketTaggingHandler
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("DeleteBucketTagging")
|
||||
// POST method handlers
|
||||
bktRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodPost, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("delete").
|
||||
Handler(named("DeleteMultipleObjects", h.DeleteMultipleObjectsHandler))).
|
||||
// todo consider add filter to match header for defaultHandler: hdrContentType, "multipart/form-data*"
|
||||
DefaultHandler(named("PostObject", h.PostObject)))
|
||||
})
|
||||
|
||||
// GetBucketObjectLockConfig
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketObjectLockConfigHandler)).
|
||||
Queries("object-lock", "").
|
||||
Name("GetBucketObjectLockConfig")
|
||||
// GetBucketVersioning
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketVersioningHandler)).
|
||||
Queries("versioning", "").
|
||||
Name("GetBucketVersioning")
|
||||
// GetBucketNotification
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.GetBucketNotificationHandler)).
|
||||
Queries("notification", "").
|
||||
Name("GetBucketNotification")
|
||||
// ListenBucketNotification
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(h.ListenBucketNotificationHandler).
|
||||
Queries("events", "{events:.*}").
|
||||
Name("ListenBucketNotification")
|
||||
// ListObjectsV2M
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.ListObjectsV2MHandler)).
|
||||
Queries("list-type", "2", "metadata", "true").
|
||||
Name("ListObjectsV2M")
|
||||
// ListObjectsV2
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.ListObjectsV2Handler)).
|
||||
Queries("list-type", "2").
|
||||
Name("ListObjectsV2")
|
||||
// ListBucketVersions
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.ListBucketObjectVersionsHandler)).
|
||||
Queries("versions", "").
|
||||
Name("ListBucketVersions")
|
||||
// ListObjectsV1 (Legacy)
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(h.ListObjectsV1Handler)).
|
||||
Name("ListObjectsV1")
|
||||
// PutBucketLifecycle
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "").
|
||||
Name("PutBucketLifecycle")
|
||||
// PutBucketEncryption
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketEncryptionHandler)).
|
||||
Queries("encryption", "").
|
||||
Name("PutBucketEncryption")
|
||||
// DELETE method handlers
|
||||
bktRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodDelete, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("cors").
|
||||
Handler(named("DeleteBucketCors", h.DeleteBucketCorsHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("website").
|
||||
Handler(named("DeleteBucketWebsite", h.DeleteBucketWebsiteHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("DeleteBucketTagging", h.DeleteBucketTaggingHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("policy").
|
||||
Handler(named("PutBucketPolicy", h.PutBucketPolicyHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("lifecycle").
|
||||
Handler(named("PutBucketLifecycle", h.PutBucketLifecycleHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("encryption").
|
||||
Handler(named("DeleteBucketEncryption", h.DeleteBucketEncryptionHandler))).
|
||||
DefaultHandler(named("DeleteBucket", h.DeleteBucketHandler)))
|
||||
})
|
||||
|
||||
// PutBucketPolicy
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketPolicyHandler)).
|
||||
Queries("policy", "").
|
||||
Name("PutBucketPolicy")
|
||||
|
||||
// PutBucketObjectLockConfig
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketObjectLockConfigHandler)).
|
||||
Queries("object-lock", "").
|
||||
Name("PutBucketObjectLockConfig")
|
||||
// PutBucketTaggingHandler
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketTaggingHandler)).
|
||||
Queries("tagging", "").
|
||||
Name("PutBucketTagging")
|
||||
// PutBucketVersioning
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketVersioningHandler)).
|
||||
Queries("versioning", "").
|
||||
Name("PutBucketVersioning")
|
||||
// PutBucketNotification
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.PutBucketNotificationHandler)).
|
||||
Queries("notification", "").
|
||||
Name("PutBucketNotification")
|
||||
// CreateBucket
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(h.CreateBucketHandler)).
|
||||
Name("CreateBucket")
|
||||
// HeadBucket
|
||||
bucket.Methods(http.MethodHead).HandlerFunc(
|
||||
m.Handle(h.HeadBucketHandler)).
|
||||
Name("HeadBucket")
|
||||
// PostPolicy
|
||||
bucket.Methods(http.MethodPost).HeadersRegexp(hdrContentType, "multipart/form-data*").HandlerFunc(
|
||||
m.Handle(h.PostObject)).
|
||||
Name("PostObject")
|
||||
// DeleteMultipleObjects
|
||||
bucket.Methods(http.MethodPost).HandlerFunc(
|
||||
m.Handle(h.DeleteMultipleObjectsHandler)).
|
||||
Queries("delete", "").
|
||||
Name("DeleteMultipleObjects")
|
||||
// DeleteBucketPolicy
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketPolicyHandler)).
|
||||
Queries("policy", "").
|
||||
Name("DeleteBucketPolicy")
|
||||
// DeleteBucketLifecycle
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "").
|
||||
Name("DeleteBucketLifecycle")
|
||||
// DeleteBucketEncryption
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketEncryptionHandler)).
|
||||
Queries("encryption", "").
|
||||
Name("DeleteBucketEncryption")
|
||||
// DeleteBucket
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(h.DeleteBucketHandler)).
|
||||
Name("DeleteBucket")
|
||||
}
|
||||
// Root operation
|
||||
|
||||
// ListBuckets
|
||||
api.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
|
||||
m.Handle(h.ListBucketsHandler)).
|
||||
Name("ListBuckets")
|
||||
|
||||
// S3 browser with signature v4 adds '//' for ListBuckets request, so rather
|
||||
// than failing with UnknownAPIRequest we simply handle it for now.
|
||||
api.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc(
|
||||
m.Handle(h.ListBucketsHandler)).
|
||||
Name("ListBuckets")
|
||||
return bktRouter
|
||||
}
|
||||
|
||||
func objectRouter(h Handler, l *zap.Logger) chi.Router {
|
||||
objRouter := chi.NewRouter()
|
||||
objRouter.Use(s3middleware.AddObjectName(l))
|
||||
|
||||
objRouter.Head("/", named("HeadObject", h.HeadObjectHandler))
|
||||
|
||||
// GET method handlers
|
||||
objRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodGet, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("uploadId").
|
||||
Handler(named("ListParts", h.ListPartsHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("acl").
|
||||
Handler(named("GetObjectACL", h.GetObjectACLHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("GetObjectTagging", h.GetObjectTaggingHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("retention").
|
||||
Handler(named("GetObjectRetention", h.GetObjectRetentionHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("legal-hold").
|
||||
Handler(named("GetObjectLegalHold", h.GetObjectLegalHoldHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("attributes").
|
||||
Handler(named("GetObjectAttributes", h.GetObjectAttributesHandler))).
|
||||
DefaultHandler(named("GetObject", h.GetObjectHandler)))
|
||||
})
|
||||
|
||||
// PUT method handlers
|
||||
objRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodPut, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Headers(AmzCopySource).
|
||||
Queries("partNumber", "uploadId").
|
||||
Handler(named("UploadPartCopy", h.UploadPartCopy))).
|
||||
Add(NewFilter().
|
||||
Queries("partNumber", "uploadId").
|
||||
Handler(named("UploadPart", h.UploadPartHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("acl").
|
||||
Handler(named("PutObjectACL", h.PutObjectACLHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("PutObjectTagging", h.PutObjectTaggingHandler))).
|
||||
Add(NewFilter().
|
||||
Headers(AmzCopySource).
|
||||
Handler(named("CopyObject", h.CopyObjectHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("retention").
|
||||
Handler(named("PutObjectRetention", h.PutObjectRetentionHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("legal-hold").
|
||||
Handler(named("PutObjectLegalHold", h.PutObjectLegalHoldHandler))).
|
||||
DefaultHandler(named("PutObject", h.PutObjectHandler)))
|
||||
})
|
||||
|
||||
// POST method handlers
|
||||
objRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodPost, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("uploadId").
|
||||
Handler(named("CompleteMultipartUpload", h.CompleteMultipartUploadHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("uploads").
|
||||
Handler(named("CreateMultipartUpload", h.CreateMultipartUploadHandler))).
|
||||
DefaultHandler(named("SelectObjectContent", h.SelectObjectContentHandler)))
|
||||
})
|
||||
|
||||
// DELETE method handlers
|
||||
objRouter.Group(func(r chi.Router) {
|
||||
r.Method(http.MethodDelete, "/", NewHandlerFilter().
|
||||
Add(NewFilter().
|
||||
Queries("uploadId").
|
||||
Handler(named("AbortMultipartUpload", h.AbortMultipartUploadHandler))).
|
||||
Add(NewFilter().
|
||||
Queries("tagging").
|
||||
Handler(named("DeleteObjectTagging", h.DeleteObjectTaggingHandler))).
|
||||
DefaultHandler(named("DeleteObject", h.DeleteObjectHandler)))
|
||||
})
|
||||
|
||||
return objRouter
|
||||
}
|
||||
|
|
133
api/router_filter.go
Normal file
133
api/router_filter.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HandlerFilters struct {
|
||||
filters []Filter
|
||||
defaultHandler http.Handler
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
queries []Pair
|
||||
headers []Pair
|
||||
h http.Handler
|
||||
}
|
||||
|
||||
type Pair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewHandlerFilter() *HandlerFilters {
|
||||
return &HandlerFilters{}
|
||||
}
|
||||
|
||||
func NewFilter() *Filter {
|
||||
return &Filter{}
|
||||
}
|
||||
|
||||
func (hf *HandlerFilters) Add(filter *Filter) *HandlerFilters {
|
||||
hf.filters = append(hf.filters, *filter)
|
||||
return hf
|
||||
}
|
||||
|
||||
// HeadersMatch adds a matcher for header values.
|
||||
// It accepts a sequence of key/value pairs. Values may define variables.
|
||||
// Panics if number of parameters is not even.
|
||||
// Supports only exact matching.
|
||||
// If the value is an empty string, it will match any value if the key is set.
|
||||
func (f *Filter) HeadersMatch(pairs ...string) *Filter {
|
||||
length := len(pairs)
|
||||
if length%2 != 0 {
|
||||
panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs))
|
||||
}
|
||||
|
||||
for i := 0; i < length; i += 2 {
|
||||
f.headers = append(f.headers, Pair{
|
||||
Key: pairs[i],
|
||||
Value: pairs[i+1],
|
||||
})
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Headers is similar to HeadersMatch but accept only header keys, set value to empty string internally.
|
||||
func (f *Filter) Headers(headers ...string) *Filter {
|
||||
for _, header := range headers {
|
||||
f.headers = append(f.headers, Pair{
|
||||
Key: header,
|
||||
Value: "",
|
||||
})
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Filter) Handler(handler http.HandlerFunc) *Filter {
|
||||
f.h = handler
|
||||
return f
|
||||
}
|
||||
|
||||
// QueriesMatch adds a matcher for URL query values.
|
||||
// It accepts a sequence of key/value pairs. Values may define variables.
|
||||
// Panics if number of parameters is not even.
|
||||
// Supports only exact matching.
|
||||
// If the value is an empty string, it will match any value if the key is set.
|
||||
func (f *Filter) QueriesMatch(pairs ...string) *Filter {
|
||||
length := len(pairs)
|
||||
if length%2 != 0 {
|
||||
panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs))
|
||||
}
|
||||
|
||||
for i := 0; i < length; i += 2 {
|
||||
f.queries = append(f.queries, Pair{
|
||||
Key: pairs[i],
|
||||
Value: pairs[i+1],
|
||||
})
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Queries is similar to QueriesMatch but accept only query keys, set value to empty string internally.
|
||||
func (f *Filter) Queries(queries ...string) *Filter {
|
||||
for _, query := range queries {
|
||||
f.queries = append(f.queries, Pair{
|
||||
Key: query,
|
||||
Value: "",
|
||||
})
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (hf *HandlerFilters) DefaultHandler(handler http.HandlerFunc) *HandlerFilters {
|
||||
hf.defaultHandler = handler
|
||||
return hf
|
||||
}
|
||||
|
||||
func (hf *HandlerFilters) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
LOOP:
|
||||
for _, filter := range hf.filters {
|
||||
for _, header := range filter.headers {
|
||||
hdrVals := r.Header.Values(header.Key)
|
||||
if len(hdrVals) == 0 || header.Value != "" && header.Value != hdrVals[0] {
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
for _, query := range filter.queries {
|
||||
queryVal := r.URL.Query().Get(query.Key)
|
||||
if !r.URL.Query().Has(query.Key) || queryVal != "" && query.Value != queryVal {
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
filter.h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
hf.defaultHandler.ServeHTTP(w, r)
|
||||
}
|
|
@ -32,7 +32,8 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
||||
treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
|
@ -59,7 +60,6 @@ type (
|
|||
bucketResolver *resolver.BucketResolver
|
||||
services []*Service
|
||||
settings *appSettings
|
||||
maxClients api.MaxClients
|
||||
|
||||
webDone chan struct{}
|
||||
wrkDone chan struct{}
|
||||
|
@ -69,6 +69,12 @@ type (
|
|||
logLevel zap.AtomicLevel
|
||||
policies *placementPolicy
|
||||
xmlDecoder *xml.DecoderProvider
|
||||
maxClient maxClientsConfig
|
||||
}
|
||||
|
||||
maxClientsConfig struct {
|
||||
deadline time.Duration
|
||||
count int
|
||||
}
|
||||
|
||||
Logger struct {
|
||||
|
@ -102,8 +108,7 @@ func newApp(ctx context.Context, log *Logger, v *viper.Viper) *App {
|
|||
webDone: make(chan struct{}, 1),
|
||||
wrkDone: make(chan struct{}, 1),
|
||||
|
||||
maxClients: newMaxClients(v),
|
||||
settings: newAppSettings(log, v),
|
||||
settings: newAppSettings(log, v),
|
||||
}
|
||||
|
||||
app.init(ctx)
|
||||
|
@ -162,6 +167,7 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings {
|
|||
logLevel: log.lvl,
|
||||
policies: policies,
|
||||
xmlDecoder: xml.NewDecoderProvider(v.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload)),
|
||||
maxClient: newMaxClients(v),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,18 +249,20 @@ func (a *App) shutdownTracing() {
|
|||
}
|
||||
}
|
||||
|
||||
func newMaxClients(cfg *viper.Viper) api.MaxClients {
|
||||
maxClientsCount := cfg.GetInt(cfgMaxClientsCount)
|
||||
if maxClientsCount <= 0 {
|
||||
maxClientsCount = defaultMaxClientsCount
|
||||
func newMaxClients(cfg *viper.Viper) maxClientsConfig {
|
||||
config := maxClientsConfig{}
|
||||
|
||||
config.count = cfg.GetInt(cfgMaxClientsCount)
|
||||
if config.count <= 0 {
|
||||
config.count = defaultMaxClientsCount
|
||||
}
|
||||
|
||||
maxClientsDeadline := cfg.GetDuration(cfgMaxClientsDeadline)
|
||||
if maxClientsDeadline <= 0 {
|
||||
maxClientsDeadline = defaultMaxClientsDeadline
|
||||
config.deadline = cfg.GetDuration(cfgMaxClientsDeadline)
|
||||
if config.deadline <= 0 {
|
||||
config.deadline = defaultMaxClientsDeadline
|
||||
}
|
||||
|
||||
return api.NewMaxClientsMiddleware(maxClientsCount, maxClientsDeadline)
|
||||
return config
|
||||
}
|
||||
|
||||
func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) {
|
||||
|
@ -491,12 +499,18 @@ func (a *App) Serve(ctx context.Context) {
|
|||
// Attach S3 API:
|
||||
domains := a.cfg.GetStringSlice(cfgListenDomains)
|
||||
a.log.Info("fetch domains, prepare to use API", zap.Strings("domains", domains))
|
||||
router := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||
api.Attach(router, domains, a.maxClients, a.api, a.ctr, a.log, a.metrics)
|
||||
|
||||
throttleOps := middleware.ThrottleOpts{
|
||||
Limit: a.settings.maxClient.count,
|
||||
BacklogTimeout: a.settings.maxClient.deadline,
|
||||
}
|
||||
|
||||
chiRouter := chi.NewRouter()
|
||||
api.AttachChi(chiRouter, domains, throttleOps, a.api, a.ctr, a.log, a.metrics)
|
||||
|
||||
// Use mux.Router as http.Handler
|
||||
srv := new(http.Server)
|
||||
srv.Handler = router
|
||||
srv.Handler = chiRouter
|
||||
srv.ErrorLog = zap.NewStdLog(a.log)
|
||||
|
||||
a.startServices()
|
||||
|
|
2
go.mod
2
go.mod
|
@ -8,8 +8,8 @@ require (
|
|||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe
|
||||
github.com/aws/aws-sdk-go v1.44.6
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/minio/sio v0.3.0
|
||||
github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d
|
||||
github.com/nspcc-dev/neo-go v0.101.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -149,6 +149,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
|
|||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
|
@ -243,8 +245,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
errorsFrost "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||
|
@ -169,7 +169,7 @@ func (w *PoolWrapper) RemoveNode(ctx context.Context, bktInfo *data.BucketInfo,
|
|||
}
|
||||
|
||||
func getBearer(ctx context.Context, bktInfo *data.BucketInfo) []byte {
|
||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
|
||||
if bd, ok := ctx.Value(middleware.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
|
||||
if bd.Gate.BearerToken != nil {
|
||||
if bd.Gate.BearerToken.Impersonate() || bktInfo.Owner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) {
|
||||
return bd.Gate.BearerToken.Marshal()
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"go.uber.org/zap"
|
||||
|
@ -1192,7 +1192,7 @@ func (c *Tree) getNode(ctx context.Context, bktInfo *data.BucketInfo, treeID str
|
|||
}
|
||||
|
||||
func (c *Tree) reqLogger(ctx context.Context) *zap.Logger {
|
||||
reqLogger := api.GetReqLog(ctx)
|
||||
reqLogger := middleware.GetReqLog(ctx)
|
||||
if reqLogger != nil {
|
||||
return reqLogger
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue