From b2a2b5478bb093745a7e23306191ee1ad78de1ab Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 11 Aug 2020 17:34:06 +0300 Subject: [PATCH 1/4] Refactoring API - api.Error should implement error interface - add method that encodes the response into ResponseWriter Signed-off-by: Evgeniy Kulikov --- api/errors.go | 4 ++++ api/response.go | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api/errors.go b/api/errors.go index c0115332a..869061418 100644 --- a/api/errors.go +++ b/api/errors.go @@ -1612,6 +1612,10 @@ func (e errorCodeMap) ToAPIErr(errCode ErrorCode) Error { return e.ToAPIErrWithErr(errCode, nil) } +func (e Error) Error() string { + return fmt.Sprintf("%s: %d => %s", e.Code, e.HTTPStatusCode, e.Description) +} + // GetAPIError provides API Error for input API error code. func GetAPIError(code ErrorCode) Error { if apiErr, ok := errorCodes[code]; ok { diff --git a/api/response.go b/api/response.go index 2abc58235..0e424623a 100644 --- a/api/response.go +++ b/api/response.go @@ -68,7 +68,11 @@ const ( hdrSSECopyKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key" ) -var deploymentID, _ = uuid.NewRandom() +var ( + deploymentID, _ = uuid.NewRandom() + + xmlHeader = []byte(xml.Header) +) // Non exhaustive list of AWS S3 standard error responses - // http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html @@ -181,6 +185,14 @@ func EncodeResponse(response interface{}) []byte { return bytesBuffer.Bytes() } +// EncodeToResponse encodes the response into ResponseWriter. +func EncodeToResponse(w http.ResponseWriter, response interface{}) error { + if _, err := w.Write(xmlHeader); err != nil { + return err + } + return xml.NewEncoder(w).Encode(response) +} + // WriteSuccessResponseXML writes success headers and response if any, // with content-type set to `application/xml`. func WriteSuccessResponseXML(w http.ResponseWriter, response []byte) { From d9f691a59a8b8dcf29d2773ef8f40d608cda56ea Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 11 Aug 2020 17:35:54 +0300 Subject: [PATCH 2/4] Refactoring API handlers - add responses for list-objects and list-buckets - set `implement me` errors more useful Signed-off-by: Evgeniy Kulikov --- api/handler/response.go | 82 +++++++++++++++++++++ api/handler/unimplemented.go | 135 ++++++++++++++++------------------- 2 files changed, 142 insertions(+), 75 deletions(-) create mode 100644 api/handler/response.go diff --git a/api/handler/response.go b/api/handler/response.go new file mode 100644 index 000000000..07628f877 --- /dev/null +++ b/api/handler/response.go @@ -0,0 +1,82 @@ +package handler + +import "encoding/xml" + +// ListBucketsResponse - format for list buckets response +type ListBucketsResponse struct { + XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"` + + Owner Owner + + // Container for one or more buckets. + Buckets struct { + Buckets []Bucket `xml:"Bucket"` + } // Buckets are nested +} + +// Bucket container for bucket metadata +type Bucket struct { + Name string + CreationDate string // time string of format "2006-01-02T15:04:05.000Z" +} + +// Owner - bucket owner/principal +type Owner struct { + ID string + DisplayName string +} + +// ListObjectsResponse - format for list objects response. +type ListObjectsResponse struct { + XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` + + Name string + Prefix string + Marker string + + // When response is truncated (the IsTruncated element value in the response + // is true), you can use the key name in this field as marker in the subsequent + // request to get next set of objects. Server lists objects in alphabetical + // order Note: This element is returned only if you have delimiter request parameter + // specified. If response does not include the NextMaker and it is truncated, + // you can use the value of the last Key in the response as the marker in the + // subsequent request to get the next set of object keys. + NextMarker string `xml:"NextMarker,omitempty"` + + MaxKeys int + Delimiter string + // A flag that indicates whether or not ListObjects returned all of the results + // that satisfied the search criteria. + IsTruncated bool + + Contents []Object + CommonPrefixes []CommonPrefix + + // Encoding type used to encode object keys in the response. + EncodingType string `xml:"EncodingType,omitempty"` +} + +// CommonPrefix container for prefix response in ListObjectsResponse +type CommonPrefix struct { + Prefix string +} + +// Object container for object metadata +type Object struct { + Key string + LastModified string // time string of format "2006-01-02T15:04:05.000Z" + ETag string + Size int64 + + // Owner of the object. + Owner Owner + + // The class of storage used to store the object. + StorageClass string + + // UserMetadata user-defined metadata + UserMetadata StringMap `xml:"UserMetadata,omitempty"` +} + +// StringMap is a map[string]string. +type StringMap map[string]string diff --git a/api/handler/unimplemented.go b/api/handler/unimplemented.go index d7155ab1d..741cefafd 100644 --- a/api/handler/unimplemented.go +++ b/api/handler/unimplemented.go @@ -3,21 +3,14 @@ package handler import ( "net/http" + "github.com/gorilla/mux" "github.com/nspcc-dev/neofs-s3-gate/api" ) -func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: "XNeoFSUnimplemented", - Description: "implement me ListBucketsHandler", - HTTPStatusCode: http.StatusNotImplemented, - }, r.URL) -} - func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -25,7 +18,7 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -33,7 +26,7 @@ func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -41,7 +34,7 @@ func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -49,7 +42,7 @@ func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -57,7 +50,7 @@ func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http. func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -65,7 +58,7 @@ func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Reque func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -73,7 +66,7 @@ func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Req func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -81,7 +74,7 @@ func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -89,7 +82,7 @@ func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -97,7 +90,7 @@ func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -105,7 +98,7 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -113,7 +106,7 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -121,7 +114,7 @@ func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -129,7 +122,7 @@ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Reque func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -137,7 +130,7 @@ func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -145,7 +138,7 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -153,7 +146,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -161,7 +154,7 @@ func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Reque func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -169,7 +162,7 @@ func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -177,7 +170,7 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -185,7 +178,7 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -193,7 +186,7 @@ func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Reques func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -201,7 +194,7 @@ func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -209,7 +202,7 @@ func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Reque func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -217,7 +210,7 @@ func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -225,7 +218,7 @@ func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -233,7 +226,7 @@ func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -241,7 +234,7 @@ func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -249,7 +242,7 @@ func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -257,7 +250,7 @@ func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -265,7 +258,7 @@ func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http. func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -273,7 +266,7 @@ func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -281,7 +274,7 @@ func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Req func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -289,7 +282,7 @@ func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -297,7 +290,7 @@ func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Requ func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -305,7 +298,7 @@ func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -313,7 +306,7 @@ func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *htt func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -321,7 +314,7 @@ func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -329,7 +322,7 @@ func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Re func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -337,7 +330,7 @@ func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -345,7 +338,7 @@ func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Req func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -353,7 +346,7 @@ func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -361,15 +354,7 @@ func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", - HTTPStatusCode: http.StatusNotImplemented, - }, r.URL) -} - -func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -377,7 +362,7 @@ func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -385,7 +370,7 @@ func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Reque func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -393,7 +378,7 @@ func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Requ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -401,7 +386,7 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -409,7 +394,7 @@ func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *htt func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -417,7 +402,7 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -425,7 +410,7 @@ func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Requ func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -433,7 +418,7 @@ func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Re func (h *handler) PutBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -441,7 +426,7 @@ func (h *handler) PutBucketHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -449,7 +434,7 @@ func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -457,7 +442,7 @@ func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -465,7 +450,7 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -473,7 +458,7 @@ func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Reque func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -481,7 +466,7 @@ func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Re func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } @@ -489,7 +474,7 @@ func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.R func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteErrorResponse(r.Context(), w, api.Error{ Code: "XNeoFSUnimplemented", - Description: "implement me", + Description: "implement me " + mux.CurrentRoute(r).GetName(), HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } From ccd481e4e24c7f5778239fc2b0789c69d064f92e Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 11 Aug 2020 17:36:52 +0300 Subject: [PATCH 3/4] Prepare list-buckets handler Signed-off-by: Evgeniy Kulikov --- api/handler/list-buckets.go | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 api/handler/list-buckets.go diff --git a/api/handler/list-buckets.go b/api/handler/list-buckets.go new file mode 100644 index 000000000..2002ec273 --- /dev/null +++ b/api/handler/list-buckets.go @@ -0,0 +1,73 @@ +package handler + +import ( + "net/http" + "time" + + "github.com/nspcc-dev/neofs-s3-gate/api" + "github.com/nspcc-dev/neofs-s3-gate/auth" + "go.uber.org/zap" +) + +func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { + var ( + res *ListBucketsResponse + rid = api.GetRequestID(r.Context()) + ) + + tkn, err := auth.GetBearerToken(r.Context()) + if err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: api.GetAPIError(api.ErrInternalError).Code, + Description: err.Error(), + HTTPStatusCode: http.StatusInternalServerError, + }, r.URL) + + return + } + + list, err := h.obj.ListBuckets(r.Context()) + if err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: api.GetAPIError(api.ErrInternalError).Code, + Description: err.Error(), + HTTPStatusCode: http.StatusInternalServerError, + }, r.URL) + + return + } + + res = &ListBucketsResponse{ + Owner: Owner{ + ID: tkn.OwnerID.String(), + DisplayName: tkn.OwnerID.String(), + }, + } + + for _, item := range list { + res.Buckets.Buckets = append(res.Buckets.Buckets, Bucket{ + Name: item.Name, + CreationDate: item.Created.Format(time.RFC3339), + }) + } + + if err = api.EncodeToResponse(w, res); err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: api.GetAPIError(api.ErrInternalError).Code, + Description: err.Error(), + HTTPStatusCode: http.StatusInternalServerError, + }, r.URL) + } +} From b14e2d54c8275412079035608cdd4e64e56c3773 Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 11 Aug 2020 17:37:20 +0300 Subject: [PATCH 4/4] Prepare list-objects handler Signed-off-by: Evgeniy Kulikov --- api/handler/list-objects.go | 137 ++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 api/handler/list-objects.go diff --git a/api/handler/list-objects.go b/api/handler/list-objects.go new file mode 100644 index 000000000..241151b70 --- /dev/null +++ b/api/handler/list-objects.go @@ -0,0 +1,137 @@ +package handler + +import ( + "net/http" + "strconv" + "time" + + "github.com/gorilla/mux" + "github.com/nspcc-dev/neofs-s3-gate/api" + "github.com/nspcc-dev/neofs-s3-gate/api/layer" + "go.uber.org/zap" +) + +type listObjectsArgs struct { + Bucket string + Delimeter string + Encode string + Marker string + MaxKeys int + Prefix string + Version string +} + +var maxObjectList = 10000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse. + +func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { + var ( + err error + arg *listObjectsArgs + res *ListObjectsResponse + rid = api.GetRequestID(r.Context()) + ) + + if arg, err = parseListObjectArgs(r); err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: api.GetAPIError(api.ErrBadRequest).Code, + Description: err.Error(), + HTTPStatusCode: http.StatusBadRequest, + }, r.URL) + + return + } + + list, err := h.obj.ListObjects(r.Context(), &layer.ListObjectsParams{ + Bucket: arg.Bucket, + Prefix: arg.Prefix, + MaxKeys: arg.MaxKeys, + Delimiter: arg.Delimeter, + }) + if err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: api.GetAPIError(api.ErrInternalError).Code, + Description: err.Error(), + HTTPStatusCode: http.StatusInternalServerError, + }, r.URL) + + return + } + + res = &ListObjectsResponse{ + Name: arg.Bucket, + EncodingType: arg.Encode, + Marker: arg.Marker, + Prefix: arg.Prefix, + MaxKeys: arg.MaxKeys, + Delimiter: arg.Delimeter, + + IsTruncated: list.IsTruncated, + NextMarker: list.NextContinuationToken, + } + + // fill common prefixes + for i := range list.Prefixes { + res.CommonPrefixes = append(res.CommonPrefixes, CommonPrefix{ + Prefix: list.Prefixes[i], + }) + } + + // fill contents + for _, obj := range list.Objects { + res.Contents = append(res.Contents, Object{ + Key: obj.Name, + Size: obj.Size, + UserMetadata: obj.Headers, + LastModified: obj.Created.Format(time.RFC3339), + + // ETag: "", + // Owner: Owner{}, + // StorageClass: "", + }) + } + + if err := api.EncodeToResponse(w, res); err != nil { + h.log.Error("something went wrong", + zap.String("request_id", rid), + zap.Error(err)) + + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: "XNeoFSUnimplemented", + Description: "implement me " + mux.CurrentRoute(r).GetName(), + HTTPStatusCode: http.StatusNotImplemented, + }, r.URL) + } +} + +func parseListObjectArgs(r *http.Request) (*listObjectsArgs, error) { + var ( + err error + res listObjectsArgs + ) + + if r.URL.Query().Get("max-keys") == "" { + res.MaxKeys = maxObjectList + } else if res.MaxKeys, err = strconv.Atoi(r.URL.Query().Get("max-keys")); err != nil { + return nil, api.GetAPIError(api.ErrInvalidMaxKeys) + } + + res.Prefix = r.URL.Query().Get("prefix") + res.Marker = r.URL.Query().Get("key-marker") + res.Delimeter = r.URL.Query().Get("delimiter") + res.Encode = r.URL.Query().Get("encoding-type") + res.Version = r.URL.Query().Get("version-id-marker") + + if info := api.GetReqInfo(r.Context()); info != nil { + res.Bucket = info.BucketName + } + + return &res, nil +}