diff --git a/api/handler/acl.go b/api/handler/acl.go index 65c3b6e..dab1f7d 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 b91960d..ba9589b 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 c5125f1..89cf8f4 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 d4a4fa1..494134c 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 3c149dc..1c22859 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 437793d..0635863 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 f6b9b7c..57f3bfc 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 a5f2605..1605dce 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 df5677f..c394918 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 7217b6f..1152ea8 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 a0e4a8a..d0c1225 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 591e747..f5ded87 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 d85b6ad..5378dd2 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 881e97d..d829187 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 422d40f..0e1986d 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 ea97c82..9fc82ee 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 5ee06ab..defd960 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 8b0f07b..b3c8f94 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 d2c1b2f..57dfdd9 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 6da9662..d315ba9 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 d65c365..a0b1727 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 c76d8db..08d4738 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 ea18749..356bb3e 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 1f7e1ca..fccdfc3 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 de89b43..a7a774f 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 20a87b3..36965eb 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/host_bucket_router.go b/api/host_bucket_router.go new file mode 100644 index 0000000..e5ceeb9 --- /dev/null +++ b/api/host_bucket_router.go @@ -0,0 +1,71 @@ +package api + +import ( + "net/http" + "strings" + + "github.com/go-chi/chi/v5" +) + +type HostBucketRouter struct { + routes map[string]chi.Router + bktParam string + defaultRouter chi.Router +} + +func NewHostBucketRouter(bktParam string) HostBucketRouter { + return HostBucketRouter{ + routes: make(map[string]chi.Router), + bktParam: bktParam, + } +} + +func (hr *HostBucketRouter) Default(router chi.Router) { + hr.defaultRouter = router +} + +func (hr HostBucketRouter) Map(host string, h chi.Router) { + hr.routes[strings.ToLower(host)] = h +} + +func (hr HostBucketRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { + bucket, domain := getBucketDomain(getHost(r)) + router, ok := hr.routes[strings.ToLower(domain)] + if !ok { + router = hr.defaultRouter + if router == nil { + http.Error(w, http.StatusText(404), 404) + return + } + } + + if rctx := chi.RouteContext(r.Context()); rctx != nil && bucket != "" { + rctx.URLParams.Add(hr.bktParam, bucket) + } + + router.ServeHTTP(w, r) +} + +func getBucketDomain(host string) (bucket string, domain string) { + parts := strings.Split(host, ".") + if len(parts) > 1 { + return parts[0], strings.Join(parts[1:], ".") + } + return "", host +} + +// getHost tries its best to return the request host. +// According to section 14.23 of RFC 2616 the Host header +// can include the port number if the default value of 80 is not used. +func getHost(r *http.Request) string { + host := r.Host + if r.URL.IsAbs() { + host = r.URL.Host + } + + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } + + return host +} diff --git a/api/layer/frostfs_mock.go b/api/layer/frostfs_mock.go index 36277ef..87ab8cb 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 7c28d4c..b28a182 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 3dbf9ba..6a3ccd3 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 c440474..fcb7bc8 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 72cd965..884f477 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/max_clients.go b/api/max_clients.go deleted file mode 100644 index 4827839..0000000 --- a/api/max_clients.go +++ /dev/null @@ -1,60 +0,0 @@ -package api - -import ( - "net/http" - "time" - - "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" -) - -type ( - // MaxClients provides HTTP handler wrapper with the client limit. - MaxClients interface { - Handle(http.HandlerFunc) http.HandlerFunc - } - - maxClients struct { - pool chan struct{} - timeout time.Duration - } -) - -const defaultRequestDeadline = time.Second * 30 - -// NewMaxClientsMiddleware returns MaxClients interface with handler wrapper based on -// the provided count and the timeout limits. -func NewMaxClientsMiddleware(count int, timeout time.Duration) MaxClients { - if timeout <= 0 { - timeout = defaultRequestDeadline - } - - return &maxClients{ - pool: make(chan struct{}, count), - timeout: timeout, - } -} - -// Handler wraps HTTP handler function with logic limiting access to it. -func (m *maxClients) Handle(f http.HandlerFunc) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if m.pool == nil { - f.ServeHTTP(w, r) - return - } - - deadline := time.NewTimer(m.timeout) - defer deadline.Stop() - - select { - case m.pool <- struct{}{}: - defer func() { <-m.pool }() - f.ServeHTTP(w, r) - case <-deadline.C: - // Send a http timeout message - WriteErrorResponse(w, GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrOperationTimedOut)) - return - case <-r.Context().Done(): - return - } - } -} diff --git a/api/user_auth.go b/api/middleware/auth.go similarity index 80% rename from api/user_auth.go rename to api/middleware/auth.go index 94ba2e6..a1c9562 100644 --- a/api/user_auth.go +++ b/api/middleware/auth.go @@ -1,4 +1,4 @@ -package api +package middleware import ( "context" @@ -6,7 +6,6 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" - "github.com/gorilla/mux" "go.uber.org/zap" ) @@ -22,8 +21,7 @@ var BoxData = KeyWrapper("__context_box_key") // ClientTime is an ID used to store client time.Time in a context. var ClientTime = KeyWrapper("__context_client_time") -// AuthMiddleware adds user authentication via center to router using log for logging. -func AuthMiddleware(log *zap.Logger, center auth.Center) mux.MiddlewareFunc { +func Auth(center auth.Center, log *zap.Logger) Func { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -51,11 +49,3 @@ func AuthMiddleware(log *zap.Logger, center auth.Center) mux.MiddlewareFunc { }) } } - -func reqLogOrDefault(ctx context.Context, log *zap.Logger) *zap.Logger { - reqLog := GetReqLog(ctx) - if reqLog != nil { - return reqLog - } - return log -} diff --git a/api/stats.go b/api/middleware/metrics.go similarity index 77% rename from api/stats.go rename to api/middleware/metrics.go index 49a2d30..de3a5c2 100644 --- a/api/stats.go +++ b/api/middleware/metrics.go @@ -1,4 +1,4 @@ -package api +package middleware import ( "context" @@ -9,12 +9,102 @@ import ( "sync/atomic" "time" + "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + "go.uber.org/zap" ) -func RequestTypeFromAPI(api string) metrics.RequestType { +type ( + UsersStat interface { + Update(user, bucket, cnrID string, reqType int, in, out uint64) + } + + readCounter struct { + io.ReadCloser + countBytes uint64 + } + + writeCounter struct { + http.ResponseWriter + countBytes uint64 + } + + responseWrapper struct { + sync.Once + http.ResponseWriter + + statusCode int + startTime time.Time + } + + // BucketResolveFunc is a func to resolve bucket info by name. + BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error) + + // cidResolveFunc is a func to resolve CID in Stats handler. + cidResolveFunc func(ctx context.Context, reqInfo *ReqInfo) (cnrID string) +) + +const systemPath = "/system" + +// Metrics wraps http handler for api with basic statistics collection. +func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics) Func { + return func(h http.Handler) http.Handler { + return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics) + } +} + +// Stats is a handler that update metrics. +func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + reqInfo := GetReqInfo(r.Context()) + + appMetrics.Statistic().CurrentS3RequestsInc(reqInfo.API) + defer appMetrics.Statistic().CurrentS3RequestsDec(reqInfo.API) + + in := &readCounter{ReadCloser: r.Body} + out := &writeCounter{ResponseWriter: w} + + r.Body = in + + statsWriter := &responseWrapper{ + ResponseWriter: out, + startTime: time.Now(), + } + + f(statsWriter, r) + + // Time duration in secs since the call started. + // We don't need to do nanosecond precision here + // simply for the fact that it is not human-readable. + durationSecs := time.Since(statsWriter.startTime).Seconds() + + user := resolveUser(r.Context()) + cnrID := resolveCID(r.Context(), reqInfo) + appMetrics.Update(user, reqInfo.BucketName, cnrID, requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes) + + code := statsWriter.statusCode + // A successful request has a 2xx response code + successReq := code >= http.StatusOK && code < http.StatusMultipleChoices + if !strings.HasSuffix(r.URL.Path, systemPath) { + appMetrics.Statistic().TotalS3RequestsInc(reqInfo.API) + if !successReq && code != 0 { + appMetrics.Statistic().TotalS3ErrorsInc(reqInfo.API) + } + } + + if r.Method == http.MethodGet { + // Increment the prometheus http request response histogram with appropriate label + appMetrics.Statistic().RequestDurationsUpdate(reqInfo.API, durationSecs) + } + + appMetrics.Statistic().TotalInputBytesAdd(in.countBytes) + appMetrics.Statistic().TotalOutputBytesAdd(out.countBytes) + } +} + +func requestTypeFromAPI(api string) metrics.RequestType { switch api { case "Options", "HeadObject", "HeadBucket": return metrics.HEADRequest @@ -43,83 +133,20 @@ func RequestTypeFromAPI(api string) metrics.RequestType { } } -type ( - UsersStat interface { - Update(user, bucket, cnrID string, reqType int, in, out uint64) - } - - readCounter struct { - io.ReadCloser - countBytes uint64 - } - - writeCounter struct { - http.ResponseWriter - countBytes uint64 - } - - responseWrapper struct { - sync.Once - http.ResponseWriter - - statusCode int - startTime time.Time - } -) - -const systemPath = "/system" - -//var apiStatMetrics = metrics.newApiStatMetrics() - -// CIDResolveFunc is a func to resolve CID in Stats handler. -type CIDResolveFunc func(ctx context.Context, reqInfo *ReqInfo) (cnrID string) - -// Stats is a handler that update metrics. -func Stats(f http.HandlerFunc, resolveCID CIDResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - reqInfo := GetReqInfo(r.Context()) - - appMetrics.Statistic().CurrentS3RequestsInc(reqInfo.API) - defer appMetrics.Statistic().CurrentS3RequestsDec(reqInfo.API) - - in := &readCounter{ReadCloser: r.Body} - out := &writeCounter{ResponseWriter: w} - - r.Body = in - - statsWriter := &responseWrapper{ - ResponseWriter: out, - startTime: time.Now(), +// resolveCID forms CIDResolveFunc using BucketResolveFunc. +func resolveCID(log *zap.Logger, resolveBucket BucketResolveFunc) cidResolveFunc { + return func(ctx context.Context, reqInfo *ReqInfo) (cnrID string) { + if reqInfo.BucketName == "" || reqInfo.API == "CreateBucket" || reqInfo.API == "" { + return "" } - f(statsWriter, r) - - // Time duration in secs since the call started. - // We don't need to do nanosecond precision here - // simply for the fact that it is not human-readable. - durationSecs := time.Since(statsWriter.startTime).Seconds() - - user := resolveUser(r.Context()) - cnrID := resolveCID(r.Context(), reqInfo) - appMetrics.Update(user, reqInfo.BucketName, cnrID, RequestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes) - - code := statsWriter.statusCode - // A successful request has a 2xx response code - successReq := code >= http.StatusOK && code < http.StatusMultipleChoices - if !strings.HasSuffix(r.URL.Path, systemPath) { - appMetrics.Statistic().TotalS3RequestsInc(reqInfo.API) - if !successReq && code != 0 { - appMetrics.Statistic().TotalS3ErrorsInc(reqInfo.API) - } + bktInfo, err := resolveBucket(ctx, reqInfo.BucketName) + if err != nil { + reqLogOrDefault(ctx, log).Debug("failed to resolve CID", zap.Error(err)) + return "" } - if r.Method == http.MethodGet { - // Increment the prometheus http request response histogram with appropriate label - appMetrics.Statistic().RequestDurationsUpdate(reqInfo.API, durationSecs) - } - - appMetrics.Statistic().TotalInputBytesAdd(in.countBytes) - appMetrics.Statistic().TotalOutputBytesAdd(out.countBytes) + return bktInfo.CID.EncodeToString() } } diff --git a/api/middleware/middleware.go b/api/middleware/middleware.go new file mode 100644 index 0000000..c09fb5b --- /dev/null +++ b/api/middleware/middleware.go @@ -0,0 +1,27 @@ +package middleware + +import ( + "context" + "net/http" + + "go.uber.org/zap" +) + +type Func func(h http.Handler) http.Handler + +func WrapHandler(handler http.HandlerFunc) Func { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler(w, r) + h.ServeHTTP(w, r) + }) + } +} + +func reqLogOrDefault(ctx context.Context, log *zap.Logger) *zap.Logger { + reqLog := GetReqLog(ctx) + if reqLog != nil { + return reqLog + } + return log +} diff --git a/api/reqinfo.go b/api/middleware/reqinfo.go similarity index 62% rename from api/reqinfo.go rename to api/middleware/reqinfo.go index 141f2f6..c541ad0 100644 --- a/api/reqinfo.go +++ b/api/middleware/reqinfo.go @@ -1,4 +1,4 @@ -package api +package middleware import ( "context" @@ -9,8 +9,10 @@ import ( "strings" "sync" - "github.com/gorilla/mux" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" "go.uber.org/zap" + "google.golang.org/grpc/metadata" ) type ( @@ -41,23 +43,30 @@ type ( Object string Method string } -) -// Key used for custom key/value in context. -type contextKeyType string + // Key used for custom key/value in context. + contextKeyType string +) const ( ctxRequestInfo = contextKeyType("FrostFS-S3-GW") ctxRequestLogger = contextKeyType("FrostFS-S3-GW-Logger") ) +const HdrAmzRequestID = "x-amz-request-id" + +const ( + BucketURLPrm = "bucket" + ObjectURLPrm = "object" +) + +var deploymentID = uuid.Must(uuid.NewRandom()) + var ( // De-facto standard header keys. xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") xRealIP = http.CanonicalHeaderKey("X-Real-IP") -) -var ( // RFC7239 defines a new "Forwarded: " header designed to replace the // existing use of X-Forwarded-* headers. // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43. @@ -67,68 +76,6 @@ var ( forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|, )]+)(.*)`) ) -// GetSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239 -// Forwarded headers (in that order), falls back to r.RemoteAddr when everything -// else fails. -func GetSourceIP(r *http.Request) string { - var addr string - - if fwd := r.Header.Get(xForwardedFor); fwd != "" { - // Only grabs the first (client) address. Note that '192.168.0.1, - // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after - // the first one may represent forwarding proxies earlier in the chain. - s := strings.Index(fwd, ", ") - if s == -1 { - s = len(fwd) - } - addr = fwd[:s] - } else if fwd := r.Header.Get(xRealIP); fwd != "" { - // X-Real-IP should only contain one IP address (the client making the - // request). - addr = fwd - } else if fwd := r.Header.Get(forwarded); fwd != "" { - // match should contain at least two elements if the protocol was - // specified in the Forwarded header. The first element will always be - // the 'for=' capture, which we ignore. In the case of multiple IP - // addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only - // extract the first, which should be the client IP. - if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { - // IPv6 addresses in Forwarded headers are quoted-strings. We strip - // these quotes. - addr = strings.Trim(match[1], `"`) - } - } - - if addr != "" { - return addr - } - - // Default to remote address if headers not set. - addr, _, _ = net.SplitHostPort(r.RemoteAddr) - return addr -} - -func prepareReqInfo(w http.ResponseWriter, r *http.Request) *ReqInfo { - vars := mux.Vars(r) - bucket := vars["bucket"] - object, err := url.PathUnescape(vars["object"]) - if err != nil { - object = vars["object"] - } - prefix, err := url.QueryUnescape(vars["prefix"]) - if err != nil { - prefix = vars["prefix"] - } - if prefix != "" { - object = prefix - } - return NewReqInfo(w, r, ObjectRequest{ - Bucket: bucket, - Object: object, - Method: mux.CurrentRoute(r).GetName(), - }) -} - // NewReqInfo returns new ReqInfo based on parameters. func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqInfo { return &ReqInfo{ @@ -136,7 +83,7 @@ func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqI BucketName: req.Bucket, ObjectName: req.Object, UserAgent: r.UserAgent(), - RemoteHost: GetSourceIP(r), + RemoteHost: getSourceIP(r), RequestID: GetRequestID(w), DeploymentID: deploymentID.String(), URL: r.URL, @@ -187,6 +134,18 @@ func (r *ReqInfo) GetTags() []KeyVal { return append([]KeyVal(nil), r.tags...) } +// GetRequestID returns the request ID from the response writer or the context. +func GetRequestID(v interface{}) string { + switch t := v.(type) { + case context.Context: + return GetReqInfo(t).RequestID + case http.ResponseWriter: + return t.Header().Get(HdrAmzRequestID) + default: + panic("unknown type") + } +} + // SetReqInfo sets ReqInfo in the context. func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context { if ctx == nil { @@ -224,3 +183,124 @@ func GetReqLog(ctx context.Context) *zap.Logger { } return nil } + +func Request(log *zap.Logger) Func { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // generate random UUIDv4 + id, _ := uuid.NewRandom() + + // set request id into response header + // also we have to set request id here + // to be able to get it in NewReqInfo + w.Header().Set(HdrAmzRequestID, id.String()) + + // set request info into context + // bucket name and object will be set in reqInfo later (limitation of go-chi) + reqInfo := NewReqInfo(w, r, ObjectRequest{}) + r = r.WithContext(SetReqInfo(r.Context(), reqInfo)) + + // set request id into gRPC meta header + r = r.WithContext(metadata.AppendToOutgoingContext( + r.Context(), HdrAmzRequestID, reqInfo.RequestID, + )) + + reqLogger := log.With(zap.String("request_id", reqInfo.RequestID)) + r = r.WithContext(SetReqLogger(r.Context(), reqLogger)) + + reqLogger.Info("request start", zap.String("host", r.Host), + zap.String("remote_host", reqInfo.RemoteHost)) + + // continue execution + h.ServeHTTP(w, r) + }) + } +} + +// AddBucketName adds bucket name to ReqInfo from context. +func AddBucketName(l *zap.Logger) Func { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + reqInfo := GetReqInfo(ctx) + reqInfo.BucketName = chi.URLParam(r, BucketURLPrm) + + reqLogger := reqLogOrDefault(ctx, l) + r = r.WithContext(SetReqLogger(ctx, reqLogger.With(zap.String("bucket", reqInfo.BucketName)))) + + h.ServeHTTP(w, r) + }) + } +} + +// AddObjectName adds objects name to ReqInfo from context. +func AddObjectName(l *zap.Logger) Func { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + obj := chi.URLParam(r, ObjectURLPrm) + object, err := url.PathUnescape(obj) + if err != nil { + object = obj + } + prefix, err := url.QueryUnescape(chi.URLParam(r, "prefix")) + if err != nil { + prefix = chi.URLParam(r, "prefix") + } + if prefix != "" { + object = prefix + } + + ctx := r.Context() + + reqInfo := GetReqInfo(ctx) + reqInfo.ObjectName = object + + reqLogger := reqLogOrDefault(ctx, l) + r = r.WithContext(SetReqLogger(ctx, reqLogger.With(zap.String("object", reqInfo.ObjectName)))) + + h.ServeHTTP(w, r) + }) + } +} + +// getSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239 +// Forwarded headers (in that order), falls back to r.RemoteAddr when everything +// else fails. +func getSourceIP(r *http.Request) string { + var addr string + + if fwd := r.Header.Get(xForwardedFor); fwd != "" { + // Only grabs the first (client) address. Note that '192.168.0.1, + // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after + // the first one may represent forwarding proxies earlier in the chain. + s := strings.Index(fwd, ", ") + if s == -1 { + s = len(fwd) + } + addr = fwd[:s] + } else if fwd := r.Header.Get(xRealIP); fwd != "" { + // X-Real-IP should only contain one IP address (the client making the + // request). + addr = fwd + } else if fwd := r.Header.Get(forwarded); fwd != "" { + // match should contain at least two elements if the protocol was + // specified in the Forwarded header. The first element will always be + // the 'for=' capture, which we ignore. In the case of multiple IP + // addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only + // extract the first, which should be the client IP. + if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { + // IPv6 addresses in Forwarded headers are quoted-strings. We strip + // these quotes. + addr = strings.Trim(match[1], `"`) + } + } + + if addr != "" { + return addr + } + + // Default to remote address if headers not set. + addr, _, _ = net.SplitHostPort(r.RemoteAddr) + return addr +} diff --git a/api/response.go b/api/middleware/response.go similarity index 87% rename from api/response.go rename to api/middleware/response.go index b85e3da..22f4c65 100644 --- a/api/response.go +++ b/api/middleware/response.go @@ -1,4 +1,4 @@ -package api +package middleware import ( "bytes" @@ -6,10 +6,11 @@ import ( "fmt" "net/http" "strconv" + "sync" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version" - "github.com/google/uuid" + "go.uber.org/zap" ) type ( @@ -34,19 +35,26 @@ type ( // Underlying HTTP status code for the returned error. StatusCode int `xml:"-" json:"-"` } + + // mimeType represents various MIME types used in API responses. + mimeType string ) const ( + + // MimeNone means no response type. + MimeNone mimeType = "" + + // MimeXML means response type is XML. + MimeXML mimeType = "application/xml" + hdrServerInfo = "Server" hdrAcceptRanges = "Accept-Ranges" hdrContentType = "Content-Type" hdrContentLength = "Content-Length" hdrRetryAfter = "Retry-After" - hdrAmzCopySource = "X-Amz-Copy-Source" - // Response request id. - hdrAmzRequestID = "x-amz-request-id" // hdrSSE is the general AWS SSE HTTP header key. hdrSSE = "X-Amz-Server-Side-Encryption" @@ -61,8 +69,6 @@ const ( ) var ( - deploymentID, _ = uuid.NewRandom() - xmlHeader = []byte(xml.Header) ) @@ -140,16 +146,6 @@ func WriteErrorResponseNoHeader(w http.ResponseWriter, reqInfo *ReqInfo, err err WriteResponseBody(w, encodedErrorResponse) } -// If none of the http routes match respond with appropriate errors. -func errorResponseHandler(w http.ResponseWriter, r *http.Request) { - desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path) - WriteErrorResponse(w, GetReqInfo(r.Context()), errors.Error{ - Code: "UnknownAPIRequest", - Description: desc, - HTTPStatusCode: http.StatusBadRequest, - }) -} - // Write http common headers. func setCommonHeaders(w http.ResponseWriter) { w.Header().Set(hdrServerInfo, version.Server) @@ -280,3 +276,56 @@ func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse { HostID: info.DeploymentID, } } + +type logResponseWriter struct { + sync.Once + http.ResponseWriter + + statusCode int +} + +func (lrw *logResponseWriter) WriteHeader(code int) { + lrw.Do(func() { + lrw.statusCode = code + lrw.ResponseWriter.WriteHeader(code) + }) +} + +func (lrw *logResponseWriter) Flush() { + if f, ok := lrw.ResponseWriter.(http.Flusher); ok { + f.Flush() + } +} + +func LogSuccessResponse(l *zap.Logger) Func { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + lw := &logResponseWriter{ResponseWriter: w} + + // here reqInfo doesn't contain bucket name and object name + + // pass execution: + h.ServeHTTP(lw, r) + + // here reqInfo contains bucket name and object name because of + // addBucketName and addObjectName middlewares + + // Ignore >400 status codes + if lw.statusCode >= http.StatusBadRequest { + return + } + + ctx := r.Context() + reqLogger := reqLogOrDefault(ctx, l) + reqInfo := GetReqInfo(ctx) + + reqLogger.Info("request end", + zap.String("method", reqInfo.API), + zap.String("bucket", reqInfo.BucketName), + zap.String("object", reqInfo.ObjectName), + zap.Int("status", lw.statusCode), + zap.String("description", http.StatusText(lw.statusCode)), + ) + }) + } +} diff --git a/api/tracing.go b/api/middleware/tracing.go similarity index 95% rename from api/tracing.go rename to api/middleware/tracing.go index 96b3bcd..c6fbd6f 100644 --- a/api/tracing.go +++ b/api/middleware/tracing.go @@ -1,4 +1,4 @@ -package api +package middleware import ( "context" @@ -6,15 +6,14 @@ import ( "sync" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" - "github.com/gorilla/mux" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.18.0" "go.opentelemetry.io/otel/trace" ) -// TracingMiddleware adds tracing support for requests. +// Tracing adds tracing support for requests. // Must be placed after prepareRequest middleware. -func TracingMiddleware() mux.MiddlewareFunc { +func Tracing() Func { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { appCtx, span := StartHTTPServerSpan(r, "REQUEST S3") diff --git a/api/router.go b/api/router.go index 0ff2d29..ac183f9 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/api/router_filter.go b/api/router_filter.go new file mode 100644 index 0000000..e7e6f47 --- /dev/null +++ b/api/router_filter.go @@ -0,0 +1,133 @@ +package api + +import ( + "fmt" + "net/http" +) + +type HandlerFilters struct { + filters []Filter + defaultHandler http.Handler +} + +type Filter struct { + queries []Pair + headers []Pair + h http.Handler +} + +type Pair struct { + Key string + Value string +} + +func NewHandlerFilter() *HandlerFilters { + return &HandlerFilters{} +} + +func NewFilter() *Filter { + return &Filter{} +} + +func (hf *HandlerFilters) Add(filter *Filter) *HandlerFilters { + hf.filters = append(hf.filters, *filter) + return hf +} + +// HeadersMatch adds a matcher for header values. +// It accepts a sequence of key/value pairs. Values may define variables. +// Panics if number of parameters is not even. +// Supports only exact matching. +// If the value is an empty string, it will match any value if the key is set. +func (f *Filter) HeadersMatch(pairs ...string) *Filter { + length := len(pairs) + if length%2 != 0 { + panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs)) + } + + for i := 0; i < length; i += 2 { + f.headers = append(f.headers, Pair{ + Key: pairs[i], + Value: pairs[i+1], + }) + } + + return f +} + +// Headers is similar to HeadersMatch but accept only header keys, set value to empty string internally. +func (f *Filter) Headers(headers ...string) *Filter { + for _, header := range headers { + f.headers = append(f.headers, Pair{ + Key: header, + Value: "", + }) + } + + return f +} + +func (f *Filter) Handler(handler http.HandlerFunc) *Filter { + f.h = handler + return f +} + +// QueriesMatch adds a matcher for URL query values. +// It accepts a sequence of key/value pairs. Values may define variables. +// Panics if number of parameters is not even. +// Supports only exact matching. +// If the value is an empty string, it will match any value if the key is set. +func (f *Filter) QueriesMatch(pairs ...string) *Filter { + length := len(pairs) + if length%2 != 0 { + panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs)) + } + + for i := 0; i < length; i += 2 { + f.queries = append(f.queries, Pair{ + Key: pairs[i], + Value: pairs[i+1], + }) + } + + return f +} + +// Queries is similar to QueriesMatch but accept only query keys, set value to empty string internally. +func (f *Filter) Queries(queries ...string) *Filter { + for _, query := range queries { + f.queries = append(f.queries, Pair{ + Key: query, + Value: "", + }) + } + + return f +} + +func (hf *HandlerFilters) DefaultHandler(handler http.HandlerFunc) *HandlerFilters { + hf.defaultHandler = handler + return hf +} + +func (hf *HandlerFilters) ServeHTTP(w http.ResponseWriter, r *http.Request) { +LOOP: + for _, filter := range hf.filters { + for _, header := range filter.headers { + hdrVals := r.Header.Values(header.Key) + if len(hdrVals) == 0 || header.Value != "" && header.Value != hdrVals[0] { + continue LOOP + } + } + for _, query := range filter.queries { + queryVal := r.URL.Query().Get(query.Key) + if !r.URL.Query().Has(query.Key) || queryVal != "" && query.Value != queryVal { + continue LOOP + } + } + filter.h.ServeHTTP(w, r) + return + } + + hf.defaultHandler.ServeHTTP(w, r) +} diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index 8093e35..43ad4cb 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" @@ -59,7 +60,6 @@ type ( bucketResolver *resolver.BucketResolver services []*Service settings *appSettings - maxClients api.MaxClients webDone chan struct{} wrkDone chan struct{} @@ -69,6 +69,12 @@ type ( logLevel zap.AtomicLevel policies *placementPolicy xmlDecoder *xml.DecoderProvider + maxClient maxClientsConfig + } + + maxClientsConfig struct { + deadline time.Duration + count int } Logger struct { @@ -102,8 +108,7 @@ func newApp(ctx context.Context, log *Logger, v *viper.Viper) *App { webDone: make(chan struct{}, 1), wrkDone: make(chan struct{}, 1), - maxClients: newMaxClients(v), - settings: newAppSettings(log, v), + settings: newAppSettings(log, v), } app.init(ctx) @@ -162,6 +167,7 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings { logLevel: log.lvl, policies: policies, xmlDecoder: xml.NewDecoderProvider(v.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload)), + maxClient: newMaxClients(v), } } @@ -243,18 +249,20 @@ func (a *App) shutdownTracing() { } } -func newMaxClients(cfg *viper.Viper) api.MaxClients { - maxClientsCount := cfg.GetInt(cfgMaxClientsCount) - if maxClientsCount <= 0 { - maxClientsCount = defaultMaxClientsCount +func newMaxClients(cfg *viper.Viper) maxClientsConfig { + config := maxClientsConfig{} + + config.count = cfg.GetInt(cfgMaxClientsCount) + if config.count <= 0 { + config.count = defaultMaxClientsCount } - maxClientsDeadline := cfg.GetDuration(cfgMaxClientsDeadline) - if maxClientsDeadline <= 0 { - maxClientsDeadline = defaultMaxClientsDeadline + config.deadline = cfg.GetDuration(cfgMaxClientsDeadline) + if config.deadline <= 0 { + config.deadline = defaultMaxClientsDeadline } - return api.NewMaxClientsMiddleware(maxClientsCount, maxClientsDeadline) + return config } func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) { @@ -491,12 +499,18 @@ func (a *App) Serve(ctx context.Context) { // Attach S3 API: domains := a.cfg.GetStringSlice(cfgListenDomains) a.log.Info("fetch domains, prepare to use API", zap.Strings("domains", domains)) - router := mux.NewRouter().SkipClean(true).UseEncodedPath() - api.Attach(router, domains, a.maxClients, a.api, a.ctr, a.log, a.metrics) + + throttleOps := middleware.ThrottleOpts{ + Limit: a.settings.maxClient.count, + BacklogTimeout: a.settings.maxClient.deadline, + } + + chiRouter := chi.NewRouter() + api.AttachChi(chiRouter, domains, throttleOps, a.api, a.ctr, a.log, a.metrics) // Use mux.Router as http.Handler srv := new(http.Server) - srv.Handler = router + srv.Handler = chiRouter srv.ErrorLog = zap.NewStdLog(a.log) a.startServices() diff --git a/go.mod b/go.mod index a1fb4d0..3e937df 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,8 @@ require ( git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe github.com/aws/aws-sdk-go v1.44.6 github.com/bluele/gcache v0.0.2 + github.com/go-chi/chi/v5 v5.0.8 github.com/google/uuid v1.3.0 - github.com/gorilla/mux v1.8.0 github.com/minio/sio v0.3.0 github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d github.com/nspcc-dev/neo-go v0.101.1 diff --git a/go.sum b/go.sum index 26654e1..fad9f51 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -243,8 +245,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go index 4aa8f24..9972259 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 11379ad..a59e660 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 }