From fc90981c0328e20bcc63462754b2cd640d6e7e54 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Wed, 5 Jul 2023 17:05:45 +0300 Subject: [PATCH] [#149] Update inner imports after moving middlewares Signed-off-by: Denis Kirillov --- api/handler/acl.go | 17 +- api/handler/acl_test.go | 9 +- api/handler/attributes.go | 5 +- api/handler/copy.go | 7 +- api/handler/cors.go | 17 +- api/handler/cors_test.go | 5 +- api/handler/delete.go | 9 +- api/handler/get.go | 3 +- api/handler/handlers_test.go | 12 +- api/handler/head.go | 7 +- api/handler/head_test.go | 3 +- api/handler/info.go | 6 +- api/handler/list.go | 6 +- api/handler/locking.go | 19 +- api/handler/locking_test.go | 7 +- api/handler/multipart_upload.go | 31 +- api/handler/not_support.go | 8 +- api/handler/notifications.go | 12 +- api/handler/object_list.go | 22 +- api/handler/put.go | 15 +- api/handler/put_test.go | 13 +- api/handler/s3reader.go | 8 +- api/handler/tagging.go | 17 +- api/handler/unimplemented.go | 28 +- api/handler/util.go | 11 +- api/handler/versioning.go | 8 +- api/layer/frostfs_mock.go | 4 +- api/layer/layer.go | 11 +- api/layer/notifications.go | 4 +- api/layer/util.go | 4 +- api/layer/versioning_test.go | 4 +- api/response.go | 282 -------- api/router.go | 795 ++++++++-------------- cmd/s3-gw/app.go | 44 +- internal/frostfs/services/pool_wrapper.go | 4 +- pkg/service/tree/tree.go | 4 +- 36 files changed, 482 insertions(+), 979 deletions(-) delete mode 100644 api/response.go diff --git a/api/handler/acl.go b/api/handler/acl.go index 65c3b6e3..dab1f7d4 100644 --- a/api/handler/acl.go +++ b/api/handler/acl.go @@ -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 { diff --git a/api/handler/acl_test.go b/api/handler/acl_test.go index b91960d8..ba9589b9 100644 --- a/api/handler/acl_test.go +++ b/api/handler/acl_test.go @@ -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) diff --git a/api/handler/attributes.go b/api/handler/attributes.go index c5125f12..89cf8f41 100644 --- a/api/handler/attributes.go +++ b/api/handler/attributes.go @@ -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) } } diff --git a/api/handler/copy.go b/api/handler/copy.go index d4a4fa17..494134c5 100644 --- a/api/handler/copy.go +++ b/api/handler/copy.go @@ -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 } diff --git a/api/handler/cors.go b/api/handler/cors.go index 3c149dc8..1c228592 100644 --- a/api/handler/cors.go +++ b/api/handler/cors.go @@ -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 } } diff --git a/api/handler/cors_test.go b/api/handler/cors_test.go index 437793da..0635863f 100644 --- a/api/handler/cors_test.go +++ b/api/handler/cors_test.go @@ -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) diff --git a/api/handler/delete.go b/api/handler/delete.go index f6b9b7cc..57f3bfc0 100644 --- a/api/handler/delete.go +++ b/api/handler/delete.go @@ -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) diff --git a/api/handler/get.go b/api/handler/get.go index a5f26055..1605dce8 100644 --- a/api/handler/get.go +++ b/api/handler/get.go @@ -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) diff --git a/api/handler/handlers_test.go b/api/handler/handlers_test.go index df5677fe..c394918b 100644 --- a/api/handler/handlers_test.go +++ b/api/handler/handlers_test.go @@ -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 } diff --git a/api/handler/head.go b/api/handler/head.go index 7217b6fc..1152ea8c 100644 --- a/api/handler/head.go +++ b/api/handler/head.go @@ -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 { diff --git a/api/handler/head_test.go b/api/handler/head_test.go index a0e4a8a6..d0c1225c 100644 --- a/api/handler/head_test.go +++ b/api/handler/head_test.go @@ -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) } diff --git a/api/handler/info.go b/api/handler/info.go index 591e747b..f5ded87a 100644 --- a/api/handler/info.go +++ b/api/handler/info.go @@ -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) } } diff --git a/api/handler/list.go b/api/handler/list.go index d85b6ad0..5378dd2b 100644 --- a/api/handler/list.go +++ b/api/handler/list.go @@ -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) } } diff --git a/api/handler/locking.go b/api/handler/locking.go index 881e97db..d8291878 100644 --- a/api/handler/locking.go +++ b/api/handler/locking.go @@ -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) } } diff --git a/api/handler/locking_test.go b/api/handler/locking_test.go index 422d40f2..0e1986de 100644 --- a/api/handler/locking_test.go +++ b/api/handler/locking_test.go @@ -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) diff --git a/api/handler/multipart_upload.go b/api/handler/multipart_upload.go index ea97c823..9fc82ee9 100644 --- a/api/handler/multipart_upload.go +++ b/api/handler/multipart_upload.go @@ -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 { diff --git a/api/handler/not_support.go b/api/handler/not_support.go index 5ee06abf..defd9609 100644 --- a/api/handler/not_support.go +++ b/api/handler/not_support.go @@ -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)) } diff --git a/api/handler/notifications.go b/api/handler/notifications.go index 8b0f07b3..b3c8f949 100644 --- a/api/handler/notifications.go +++ b/api/handler/notifications.go @@ -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 } diff --git a/api/handler/object_list.go b/api/handler/object_list.go index d2c1b2f9..57dfdd97 100644 --- a/api/handler/object_list.go +++ b/api/handler/object_list.go @@ -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 diff --git a/api/handler/put.go b/api/handler/put.go index 6da96624..d315ba98 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -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 { diff --git a/api/handler/put_test.go b/api/handler/put_test.go index d65c365e..a0b17272 100644 --- a/api/handler/put_test.go +++ b/api/handler/put_test.go @@ -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, }, diff --git a/api/handler/s3reader.go b/api/handler/s3reader.go index c76d8db9..08d4738e 100644 --- a/api/handler/s3reader.go +++ b/api/handler/s3reader.go @@ -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) } diff --git a/api/handler/tagging.go b/api/handler/tagging.go index ea187498..356bb3e7 100644 --- a/api/handler/tagging.go +++ b/api/handler/tagging.go @@ -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 { diff --git a/api/handler/unimplemented.go b/api/handler/unimplemented.go index 1f7e1caf..fccdfc32 100644 --- a/api/handler/unimplemented.go +++ b/api/handler/unimplemented.go @@ -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)) } diff --git a/api/handler/util.go b/api/handler/util.go index de89b435..a7a774f2 100644 --- a/api/handler/util.go +++ b/api/handler/util.go @@ -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), diff --git a/api/handler/versioning.go b/api/handler/versioning.go index 20a87b3a..36965eb6 100644 --- a/api/handler/versioning.go +++ b/api/handler/versioning.go @@ -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) } } diff --git a/api/layer/frostfs_mock.go b/api/layer/frostfs_mock.go index 36277efc..87ab8cb5 100644 --- a/api/layer/frostfs_mock.go +++ b/api/layer/frostfs_mock.go @@ -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) } diff --git a/api/layer/layer.go b/api/layer/layer.go index 7c28d4c1..b28a182f 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -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 diff --git a/api/layer/notifications.go b/api/layer/notifications.go index 3dbf9bab..6a3ccd38 100644 --- a/api/layer/notifications.go +++ b/api/layer/notifications.go @@ -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 diff --git a/api/layer/util.go b/api/layer/util.go index c440474d..fcb7bc81 100644 --- a/api/layer/util.go +++ b/api/layer/util.go @@ -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") } diff --git a/api/layer/versioning_test.go b/api/layer/versioning_test.go index 72cd965b..884f477d 100644 --- a/api/layer/versioning_test.go +++ b/api/layer/versioning_test.go @@ -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(), diff --git a/api/response.go b/api/response.go deleted file mode 100644 index b85e3da1..00000000 --- a/api/response.go +++ /dev/null @@ -1,282 +0,0 @@ -package api - -import ( - "bytes" - "encoding/xml" - "fmt" - "net/http" - "strconv" - - "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" - "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version" - "github.com/google/uuid" -) - -type ( - // ErrorResponse -- error response format. - ErrorResponse struct { - XMLName xml.Name `xml:"Error" json:"-"` - Code string - Message string - Key string `xml:"Key,omitempty" json:"Key,omitempty"` - BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"` - Resource string - RequestID string `xml:"RequestId" json:"RequestId"` - HostID string `xml:"HostId" json:"HostId"` - - // The region where the bucket is located. This header is returned - // only in HEAD bucket and ListObjects response. - Region string `xml:"Region,omitempty" json:"Region,omitempty"` - - // Captures the server string returned in response header. - Server string `xml:"-" json:"-"` - - // Underlying HTTP status code for the returned error. - StatusCode int `xml:"-" json:"-"` - } -) - -const ( - 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" - - // hdrSSECustomerKey is the HTTP header key referencing the - // SSE-C client-provided key.. - hdrSSECustomerKey = hdrSSE + "-Customer-Key" - - // hdrSSECopyKey is the HTTP header key referencing the SSE-C - // client-provided key for SSE-C copy requests. - hdrSSECopyKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key" -) - -var ( - deploymentID, _ = uuid.NewRandom() - - xmlHeader = []byte(xml.Header) -) - -// Non exhaustive list of AWS S3 standard error responses - -// http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html -var s3ErrorResponseMap = map[string]string{ - "AccessDenied": "Access Denied.", - "BadDigest": "The Content-Md5 you specified did not match what we received.", - "EntityTooSmall": "Your proposed upload is smaller than the minimum allowed object size.", - "EntityTooLarge": "Your proposed upload exceeds the maximum allowed object size.", - "IncompleteBody": "You did not provide the number of bytes specified by the Content-Length HTTP header.", - "InternalError": "We encountered an internal error, please try again.", - "InvalidAccessKeyId": "The access key ID you provided does not exist in our records.", - "InvalidBucketName": "The specified bucket is not valid.", - "InvalidDigest": "The Content-Md5 you specified is not valid.", - "InvalidRange": "The requested range is not satisfiable", - "MalformedXML": "The XML you provided was not well-formed or did not validate against our published schema.", - "MissingContentLength": "You must provide the Content-Length HTTP header.", - "MissingContentMD5": "Missing required header for this request: Content-Md5.", - "MissingRequestBodyError": "Request body is empty.", - "NoSuchBucket": "The specified bucket does not exist.", - "NoSuchBucketPolicy": "The bucket policy does not exist", - "NoSuchKey": "The specified key does not exist.", - "NoSuchUpload": "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", - "NotImplemented": "A header you provided implies functionality that is not implemented", - "PreconditionFailed": "At least one of the pre-conditions you specified did not hold", - "RequestTimeTooSkewed": "The difference between the request time and the server's time is too large.", - "SignatureDoesNotMatch": "The request signature we calculated does not match the signature you provided. Check your key and signing method.", - "MethodNotAllowed": "The specified method is not allowed against this resource.", - "InvalidPart": "One or more of the specified parts could not be found.", - "InvalidPartOrder": "The list of parts was not in ascending order. The parts list must be specified in order by part number.", - "InvalidObjectState": "The operation is not valid for the current state of the object.", - "AuthorizationHeaderMalformed": "The authorization header is malformed; the region is wrong.", - "MalformedPOSTRequest": "The body of your POST request is not well-formed multipart/form-data.", - "BucketNotEmpty": "The bucket you tried to delete is not empty", - "AllAccessDisabled": "All access to this bucket has been disabled.", - "MalformedPolicy": "Policy has invalid resource.", - "MissingFields": "Missing fields in request.", - "AuthorizationQueryParametersError": "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"/YYYYMMDD/REGION/SERVICE/aws4_request\".", - "MalformedDate": "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.", - "BucketAlreadyOwnedByYou": "Your previous request to create the named bucket succeeded and you already own it.", - "InvalidDuration": "Duration provided in the request is invalid.", - "XAmzContentSHA256Mismatch": "The provided 'x-amz-content-sha256' header does not match what was computed.", - // Add new API errors here. -} - -// WriteErrorResponse writes error headers. -func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) int { - code := http.StatusInternalServerError - - if e, ok := err.(errors.Error); ok { - code = e.HTTPStatusCode - - switch e.Code { - case "SlowDown", "XFrostFSServerNotInitialized", "XFrostFSReadQuorum", "XFrostFSWriteQuorum": - // Set retry-after header to indicate user-agents to retry request after 120secs. - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set(hdrRetryAfter, "120") - case "AccessDenied": - // TODO process when the request is from browser and also if browser - } - } - - // Generates error response. - errorResponse := getAPIErrorResponse(reqInfo, err) - encodedErrorResponse := EncodeResponse(errorResponse) - WriteResponse(w, code, encodedErrorResponse, MimeXML) - return code -} - -// WriteErrorResponseNoHeader writes XML encoded error to the response body. -func WriteErrorResponseNoHeader(w http.ResponseWriter, reqInfo *ReqInfo, err error) { - errorResponse := getAPIErrorResponse(reqInfo, err) - encodedErrorResponse := EncodeResponse(errorResponse) - 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) - w.Header().Set(hdrAcceptRanges, "bytes") - - // Remove sensitive information - removeSensitiveHeaders(w.Header()) -} - -// removeSensitiveHeaders removes confidential encryption -// information -- e.g. the SSE-C key -- from the HTTP headers. -// It has the same semantics as RemoveSensitiveEntries. -func removeSensitiveHeaders(h http.Header) { - h.Del(hdrSSECustomerKey) - h.Del(hdrSSECopyKey) -} - -// WriteResponse writes given statusCode and response into w (with mType header if set). -func WriteResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { - setCommonHeaders(w) - if mType != MimeNone { - w.Header().Set(hdrContentType, string(mType)) - } - w.Header().Set(hdrContentLength, strconv.Itoa(len(response))) - w.WriteHeader(statusCode) - if response == nil { - return - } - - WriteResponseBody(w, response) -} - -// WriteResponseBody writes response into w. -func WriteResponseBody(w http.ResponseWriter, response []byte) { - _, _ = w.Write(response) - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } -} - -// EncodeResponse encodes the response headers into XML format. -func EncodeResponse(response interface{}) []byte { - var bytesBuffer bytes.Buffer - bytesBuffer.WriteString(xml.Header) - _ = xml. - NewEncoder(&bytesBuffer). - Encode(response) - return bytesBuffer.Bytes() -} - -// EncodeResponseNoHeader encodes response without setting xml.Header. -// Should be used with periodicXMLWriter which sends xml.Header to the client -// with whitespaces to keep connection alive. -func EncodeResponseNoHeader(response interface{}) []byte { - var bytesBuffer bytes.Buffer - _ = xml.NewEncoder(&bytesBuffer).Encode(response) - return bytesBuffer.Bytes() -} - -// EncodeToResponse encodes the response into ResponseWriter. -func EncodeToResponse(w http.ResponseWriter, response interface{}) error { - w.WriteHeader(http.StatusOK) - - if _, err := w.Write(xmlHeader); err != nil { - return fmt.Errorf("write headers: %w", err) - } - - return EncodeToResponseNoHeader(w, response) -} - -// EncodeToResponseNoHeader encodes the response into ResponseWriter without -// header status. -func EncodeToResponseNoHeader(w http.ResponseWriter, response interface{}) error { - if err := xml.NewEncoder(w).Encode(response); err != nil { - return fmt.Errorf("encode xml response: %w", err) - } - - return nil -} - -// // WriteSuccessResponseXML writes success headers and response if any, -// // with content-type set to `application/xml`. -// func WriteSuccessResponseXML(w http.ResponseWriter, response []byte) { -// WriteResponse(w, http.StatusOK, response, MimeXML) -// } - -// WriteSuccessResponseHeadersOnly writes HTTP (200) OK response with no data -// to the client. -func WriteSuccessResponseHeadersOnly(w http.ResponseWriter) { - WriteResponse(w, http.StatusOK, nil, MimeNone) -} - -// Error -- Returns S3 error string. -func (e ErrorResponse) Error() string { - if e.Message == "" { - msg, ok := s3ErrorResponseMap[e.Code] - if !ok { - msg = fmt.Sprintf("Error response code %s.", e.Code) - } - return msg - } - return e.Message -} - -// getErrorResponse gets in standard error and resource value and -// provides an encodable populated response values. -func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse { - code := "InternalError" - desc := err.Error() - - if e, ok := err.(errors.Error); ok { - code = e.Code - desc = e.Description - } - - var resource string - if info.URL != nil { - resource = info.URL.Path - } - - return ErrorResponse{ - Code: code, - Message: desc, - BucketName: info.BucketName, - Key: info.ObjectName, - Resource: resource, - RequestID: info.RequestID, - HostID: info.DeploymentID, - } -} diff --git a/api/router.go b/api/router.go index 0ff2d29d..ac183f96 100644 --- a/api/router.go +++ b/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 } diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index d002a413..b68ff9d9 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -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" @@ -58,7 +59,6 @@ type ( bucketResolver *resolver.BucketResolver services []*Service settings *appSettings - maxClients api.MaxClients webDone chan struct{} wrkDone chan struct{} @@ -68,6 +68,12 @@ type ( logLevel zap.AtomicLevel policies *placementPolicy xmlDecoder *xml.DecoderProvider + maxClient maxClientsConfig + } + + maxClientsConfig struct { + deadline time.Duration + count int } Logger struct { @@ -101,8 +107,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) @@ -161,6 +166,7 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings { logLevel: log.lvl, policies: policies, xmlDecoder: xml.NewDecoderProvider(v.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload)), + maxClient: newMaxClients(v), } } @@ -242,18 +248,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) { @@ -490,12 +498,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() diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go index 4aa8f247..99722597 100644 --- a/internal/frostfs/services/pool_wrapper.go +++ b/internal/frostfs/services/pool_wrapper.go @@ -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() diff --git a/pkg/service/tree/tree.go b/pkg/service/tree/tree.go index 11379ad4..a59e660e 100644 --- a/pkg/service/tree/tree.go +++ b/pkg/service/tree/tree.go @@ -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 }