From 16296490ee4552676c838017f8c7c91ce08dbc44 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Wed, 15 Sep 2021 12:32:26 +0300 Subject: [PATCH] [#264] Adopt minio code distributed with AGPLv3 license in gateway components Signed-off-by: Alex Vanin --- api/errors/errors.go | 1916 +++++++++++++++++++++++++++++++ api/handler/s3encoder.go | 110 ++ api/minio/api-errors.go | 2235 ------------------------------------- api/minio/api-response.go | 842 -------------- api/minio/api-utils.go | 108 -- api/minio/header.go | 86 -- api/minio/proxy.go | 127 --- api/minio/reqinfo.go | 144 --- api/reqinfo.go | 189 ++++ api/response.go | 107 ++ 10 files changed, 2322 insertions(+), 3542 deletions(-) delete mode 100644 api/minio/api-errors.go delete mode 100644 api/minio/api-response.go delete mode 100644 api/minio/api-utils.go delete mode 100644 api/minio/header.go delete mode 100644 api/minio/proxy.go delete mode 100644 api/minio/reqinfo.go diff --git a/api/errors/errors.go b/api/errors/errors.go index 6e85862..aeda1ca 100644 --- a/api/errors/errors.go +++ b/api/errors/errors.go @@ -2,12 +2,15 @@ package errors import ( "fmt" + "net/http" ) type ( // ErrorCode type of error status. ErrorCode int + errorCodeMap map[ErrorCode]Error + // Error structure represents API error. Error struct { ErrCode ErrorCode @@ -19,16 +22,1929 @@ type ( const maxEConfigJSONSize = 262272 +// Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html +const ( + _ ErrorCode = iota + ErrAccessDenied + ErrBadDigest + ErrEntityTooSmall + ErrEntityTooLarge + ErrPolicyTooLarge + ErrIllegalVersioningConfigurationException + ErrIncompleteBody + ErrInternalError + ErrInvalidAccessKeyID + ErrInvalidBucketName + ErrInvalidDigest + ErrInvalidRange + ErrInvalidCopyPartRange + ErrInvalidCopyPartRangeSource + ErrInvalidMaxKeys + ErrInvalidEncodingMethod + ErrInvalidMaxUploads + ErrInvalidMaxParts + ErrInvalidPartNumberMarker + ErrInvalidRequestBody + ErrInvalidCopySource + ErrInvalidMetadataDirective + ErrInvalidCopyDest + ErrInvalidPolicyDocument + ErrInvalidObjectState + ErrMalformedXML + ErrMissingContentLength + ErrMissingContentMD5 + ErrMissingRequestBodyError + ErrMissingSecurityHeader + ErrNoSuchBucket + ErrNoSuchBucketPolicy + ErrNoSuchBucketLifecycle + ErrNoSuchLifecycleConfiguration + ErrNoSuchBucketSSEConfig + ErrNoSuchCORSConfiguration + ErrNoSuchWebsiteConfiguration + ErrReplicationConfigurationNotFoundError + ErrNoSuchKey + ErrNoSuchUpload + ErrNoSuchVersion + ErrInvalidVersion + ErrInvalidArgument + ErrInvalidTagKey + ErrInvalidTagValue + ErrInvalidTagsSizeExceed + ErrNotImplemented + ErrPreconditionFailed + ErrNotModified + ErrRequestTimeTooSkewed + ErrSignatureDoesNotMatch + ErrMethodNotAllowed + ErrInvalidPart + ErrInvalidPartOrder + ErrAuthorizationHeaderMalformed + ErrMalformedPOSTRequest + ErrPOSTFileRequired + ErrSignatureVersionNotSupported + ErrBucketNotEmpty + ErrAllAccessDisabled + ErrMalformedPolicy + ErrMissingFields + ErrMissingCredTag + ErrCredMalformed + ErrInvalidRegion + ErrInvalidServiceS3 + ErrInvalidServiceSTS + ErrInvalidRequestVersion + ErrMissingSignTag + ErrMissingSignHeadersTag + ErrMalformedDate + ErrMalformedPresignedDate + ErrMalformedCredentialDate + ErrMalformedCredentialRegion + ErrMalformedExpires + ErrNegativeExpires + ErrAuthHeaderEmpty + ErrExpiredPresignRequest + ErrRequestNotReadyYet + ErrUnsignedHeaders + ErrMissingDateHeader + ErrInvalidQuerySignatureAlgo + ErrInvalidQueryParams + ErrBucketAlreadyOwnedByYou + ErrInvalidDuration + ErrBucketAlreadyExists + ErrMetadataTooLarge + ErrUnsupportedMetadata + ErrMaximumExpires + ErrSlowDown + ErrInvalidPrefixMarker + ErrBadRequest + ErrKeyTooLongError + ErrInvalidBucketObjectLockConfiguration + ErrObjectLockConfigurationNotFound + ErrObjectLockConfigurationNotAllowed + ErrNoSuchObjectLockConfiguration + ErrObjectLocked + ErrInvalidRetentionDate + ErrPastObjectLockRetainDate + ErrUnknownWORMModeDirective + ErrBucketTaggingNotFound + ErrObjectLockInvalidHeaders + ErrInvalidTagDirective + // Add new error codes here. + ErrNotSupported + + // SSE-S3 related API errors. + ErrInvalidEncryptionMethod + + // Server-Side-Encryption (with Customer provided key) related API errors. + ErrInsecureSSECustomerRequest + ErrSSEMultipartEncrypted + ErrSSEEncryptedObject + ErrInvalidEncryptionParameters + ErrInvalidSSECustomerAlgorithm + ErrInvalidSSECustomerKey + ErrMissingSSECustomerKey + ErrMissingSSECustomerKeyMD5 + ErrSSECustomerKeyMD5Mismatch + ErrInvalidSSECustomerParameters + ErrIncompatibleEncryptionMethod + ErrKMSNotConfigured + ErrKMSAuthFailure + + ErrNoAccessKey + ErrInvalidToken + + // Bucket notification related errors. + ErrEventNotification + ErrARNNotification + ErrRegionNotification + ErrOverlappingFilterNotification + ErrFilterNameInvalid + ErrFilterNamePrefix + ErrFilterNameSuffix + ErrFilterValueInvalid + ErrOverlappingConfigs + ErrUnsupportedNotification + + // S3 extended errors. + ErrContentSHA256Mismatch + + // Add new extended error codes here. + + // MinIO extended errors. + // ErrReadQuorum + // ErrWriteQuorum + ErrParentIsObject + ErrStorageFull + ErrRequestBodyParse + ErrObjectExistsAsDirectory + ErrInvalidObjectName + ErrInvalidObjectNamePrefixSlash + ErrInvalidResourceName + ErrServerNotInitialized + ErrOperationTimedOut + ErrOperationMaxedOut + ErrInvalidRequest + // MinIO storage class error codes. + ErrInvalidStorageClass + ErrBackendDown + // Add new extended error codes here. + // Please open a https://github.com/minio/minio/issues before adding + // new error codes here. + + ErrMalformedJSON + ErrAdminNoSuchUser + ErrAdminNoSuchGroup + ErrAdminGroupNotEmpty + ErrAdminNoSuchPolicy + ErrAdminInvalidArgument + ErrAdminInvalidAccessKey + ErrAdminInvalidSecretKey + ErrAdminConfigNoQuorum + ErrAdminConfigTooLarge + ErrAdminConfigBadJSON + ErrAdminConfigDuplicateKeys + ErrAdminCredentialsMismatch + ErrInsecureClientRequest + ErrObjectTampered + // Bucket Quota error codes. + ErrAdminBucketQuotaExceeded + ErrAdminNoSuchQuotaConfiguration + ErrAdminBucketQuotaDisabled + + ErrHealNotImplemented + ErrHealNoSuchProcess + ErrHealInvalidClientToken + ErrHealMissingBucket + ErrHealAlreadyRunning + ErrHealOverlappingPaths + ErrIncorrectContinuationToken + + // S3 Select Errors. + ErrEmptyRequestBody + ErrUnsupportedFunction + ErrInvalidExpressionType + ErrBusy + ErrUnauthorizedAccess + ErrExpressionTooLong + ErrIllegalSQLFunctionArgument + ErrInvalidKeyPath + ErrInvalidCompressionFormat + ErrInvalidFileHeaderInfo + ErrInvalidJSONType + ErrInvalidQuoteFields + ErrInvalidRequestParameter + ErrInvalidDataType + ErrInvalidTextEncoding + ErrInvalidDataSource + ErrInvalidTableAlias + ErrMissingRequiredParameter + ErrObjectSerializationConflict + ErrUnsupportedSQLOperation + ErrUnsupportedSQLStructure + ErrUnsupportedSyntax + ErrUnsupportedRangeHeader + ErrLexerInvalidChar + ErrLexerInvalidOperator + ErrLexerInvalidLiteral + ErrLexerInvalidIONLiteral + ErrParseExpectedDatePart + ErrParseExpectedKeyword + ErrParseExpectedTokenType + ErrParseExpected2TokenTypes + ErrParseExpectedNumber + ErrParseExpectedRightParenBuiltinFunctionCall + ErrParseExpectedTypeName + ErrParseExpectedWhenClause + ErrParseUnsupportedToken + ErrParseUnsupportedLiteralsGroupBy + ErrParseExpectedMember + ErrParseUnsupportedSelect + ErrParseUnsupportedCase + ErrParseUnsupportedCaseClause + ErrParseUnsupportedAlias + ErrParseUnsupportedSyntax + ErrParseUnknownOperator + ErrParseMissingIdentAfterAt + ErrParseUnexpectedOperator + ErrParseUnexpectedTerm + ErrParseUnexpectedToken + ErrParseUnexpectedKeyword + ErrParseExpectedExpression + ErrParseExpectedLeftParenAfterCast + ErrParseExpectedLeftParenValueConstructor + ErrParseExpectedLeftParenBuiltinFunctionCall + ErrParseExpectedArgumentDelimiter + ErrParseCastArity + ErrParseInvalidTypeParam + ErrParseEmptySelect + ErrParseSelectMissingFrom + ErrParseExpectedIdentForGroupName + ErrParseExpectedIdentForAlias + ErrParseUnsupportedCallWithStar + ErrParseNonUnaryAgregateFunctionCall + ErrParseMalformedJoin + ErrParseExpectedIdentForAt + ErrParseAsteriskIsNotAloneInSelectList + ErrParseCannotMixSqbAndWildcardInSelectList + ErrParseInvalidContextForWildcardInSelectList + ErrIncorrectSQLFunctionArgumentType + ErrValueParseFailure + ErrEvaluatorInvalidArguments + ErrIntegerOverflow + ErrLikeInvalidInputs + ErrCastFailed + ErrInvalidCast + ErrEvaluatorInvalidTimestampFormatPattern + ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing + ErrEvaluatorTimestampFormatPatternDuplicateFields + ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch + ErrEvaluatorUnterminatedTimestampFormatPatternToken + ErrEvaluatorInvalidTimestampFormatPatternToken + ErrEvaluatorInvalidTimestampFormatPatternSymbol + ErrEvaluatorBindingDoesNotExist + ErrMissingHeaders + ErrInvalidColumnIndex + + ErrAdminConfigNotificationTargetsFailed + ErrAdminProfilerNotEnabled + ErrInvalidDecompressedSize + ErrAddUserInvalidArgument + ErrAdminAccountNotEligible + ErrServiceAccountNotFound + ErrPostPolicyConditionInvalidFormat +) + +// error code to Error structure, these fields carry respective +// descriptions for all the error responses. +var errorCodes = errorCodeMap{ + ErrInvalidCopyDest: { + ErrCode: ErrInvalidCopyDest, + Code: "InvalidRequest", + Description: "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidCopySource: { + ErrCode: ErrInvalidCopySource, + Code: "InvalidArgument", + Description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidMetadataDirective: { + ErrCode: ErrInvalidMetadataDirective, + Code: "InvalidArgument", + Description: "Unknown metadata directive.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidStorageClass: { + ErrCode: ErrInvalidStorageClass, + Code: "InvalidStorageClass", + Description: "Invalid storage class.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidRequestBody: { + ErrCode: ErrInvalidRequestBody, + Code: "InvalidArgument", + Description: "Body shouldn't be set for this request.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidMaxUploads: { + ErrCode: ErrInvalidMaxUploads, + Code: "InvalidArgument", + Description: "Argument max-uploads must be an integer between 0 and 2147483647", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidMaxKeys: { + ErrCode: ErrInvalidMaxKeys, + Code: "InvalidArgument", + Description: "Argument maxKeys must be an integer between 0 and 2147483647", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidEncodingMethod: { + ErrCode: ErrInvalidEncodingMethod, + Code: "InvalidArgument", + Description: "Invalid Encoding Method specified in Request", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidMaxParts: { + ErrCode: ErrInvalidMaxParts, + Code: "InvalidArgument", + Description: "Argument max-parts must be an integer between 0 and 2147483647", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidPartNumberMarker: { + ErrCode: ErrInvalidPartNumberMarker, + Code: "InvalidArgument", + Description: "Argument partNumberMarker must be an integer.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidPolicyDocument: { + ErrCode: ErrInvalidPolicyDocument, + Code: "InvalidPolicyDocument", + Description: "The content of the form does not meet the conditions specified in the policy document.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAccessDenied: { + ErrCode: ErrAccessDenied, + Code: "AccessDenied", + Description: "Access Denied.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrBadDigest: { + ErrCode: ErrBadDigest, + Code: "BadDigest", + Description: "The Content-Md5 you specified did not match what we received.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEntityTooSmall: { + ErrCode: ErrEntityTooSmall, + Code: "EntityTooSmall", + Description: "Your proposed upload is smaller than the minimum allowed object size.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEntityTooLarge: { + ErrCode: ErrEntityTooLarge, + Code: "EntityTooLarge", + Description: "Your proposed upload exceeds the maximum allowed object size.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrPolicyTooLarge: { + ErrCode: ErrPolicyTooLarge, + Code: "PolicyTooLarge", + Description: "Policy exceeds the maximum allowed document size.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIllegalVersioningConfigurationException: { + ErrCode: ErrIllegalVersioningConfigurationException, + Code: "IllegalVersioningConfigurationException", + Description: "Indicates that the versioning configuration specified in the request is invalid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIncompleteBody: { + ErrCode: ErrIncompleteBody, + Code: "IncompleteBody", + Description: "You did not provide the number of bytes specified by the Content-Length HTTP header.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInternalError: { + ErrCode: ErrInternalError, + Code: "InternalError", + Description: "We encountered an internal error, please try again.", + HTTPStatusCode: http.StatusInternalServerError, + }, + ErrInvalidAccessKeyID: { + ErrCode: ErrInvalidAccessKeyID, + Code: "InvalidAccessKeyId", + Description: "The Access Key Id you provided does not exist in our records.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrInvalidBucketName: { + ErrCode: ErrInvalidBucketName, + Code: "InvalidBucketName", + Description: "The specified bucket is not valid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidDigest: { + ErrCode: ErrInvalidDigest, + Code: "InvalidDigest", + Description: "The Content-Md5 you specified is not valid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidRange: { + ErrCode: ErrInvalidRange, + Code: "InvalidRange", + Description: "The requested range is not satisfiable", + HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable, + }, + ErrMalformedXML: { + ErrCode: ErrMalformedXML, + Code: "MalformedXML", + Description: "The XML you provided was not well-formed or did not validate against our published schema.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingContentLength: { + ErrCode: ErrMissingContentLength, + Code: "MissingContentLength", + Description: "You must provide the Content-Length HTTP header.", + HTTPStatusCode: http.StatusLengthRequired, + }, + ErrMissingContentMD5: { + ErrCode: ErrMissingContentMD5, + Code: "MissingContentMD5", + Description: "Missing required header for this request: Content-Md5.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingSecurityHeader: { + ErrCode: ErrMissingSecurityHeader, + Code: "MissingSecurityHeader", + Description: "Your request was missing a required header", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingRequestBodyError: { + ErrCode: ErrMissingRequestBodyError, + Code: "MissingRequestBodyError", + Description: "Request body is empty.", + HTTPStatusCode: http.StatusLengthRequired, + }, + ErrNoSuchBucket: { + ErrCode: ErrNoSuchBucket, + Code: "NoSuchBucket", + Description: "The specified bucket does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchBucketPolicy: { + ErrCode: ErrNoSuchBucketPolicy, + Code: "NoSuchBucketPolicy", + Description: "The bucket policy does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchBucketLifecycle: { + ErrCode: ErrNoSuchBucketLifecycle, + Code: "NoSuchBucketLifecycle", + Description: "The bucket lifecycle configuration does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchLifecycleConfiguration: { + ErrCode: ErrNoSuchLifecycleConfiguration, + Code: "NoSuchLifecycleConfiguration", + Description: "The lifecycle configuration does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchBucketSSEConfig: { + ErrCode: ErrNoSuchBucketSSEConfig, + Code: "ServerSideEncryptionConfigurationNotFoundError", + Description: "The server side encryption configuration was not found", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchKey: { + ErrCode: ErrNoSuchKey, + Code: "NoSuchKey", + Description: "The specified key does not exist.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchUpload: { + ErrCode: ErrNoSuchUpload, + Code: "NoSuchUpload", + Description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchVersion: { + ErrCode: ErrNoSuchVersion, + Code: "NoSuchVersion", + Description: "Indicates that the version ID specified in the request does not match an existing version.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrInvalidVersion: { + ErrCode: ErrInvalidVersion, + Code: "InvalidArgument", + Description: "Invalid version id specified", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidArgument: { + ErrCode: ErrInvalidArgument, + Code: "InvalidArgument", + Description: "The specified argument was invalid", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTagKey: { + ErrCode: ErrInvalidTagKey, + Code: "InvalidTag", + Description: "The TagValue you have provided is invalid", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTagValue: { + ErrCode: ErrInvalidTagValue, + Code: "InvalidTag", + Description: "The TagKey you have provided is invalid", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTagsSizeExceed: { + ErrCode: ErrInvalidTagsSizeExceed, + Code: "BadRequest", + Description: "Object tags cannot be greater than 10", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrNotImplemented: { + ErrCode: ErrNotImplemented, + Code: "NotImplemented", + Description: "A header you provided implies functionality that is not implemented", + HTTPStatusCode: http.StatusNotImplemented, + }, + ErrPreconditionFailed: { + ErrCode: ErrPreconditionFailed, + Code: "PreconditionFailed", + Description: "At least one of the pre-conditions you specified did not hold", + HTTPStatusCode: http.StatusPreconditionFailed, + }, + ErrNotModified: { + ErrCode: ErrNotModified, + Code: "NotModified", + Description: "The resource was not changed.", + HTTPStatusCode: http.StatusNotModified, + }, + ErrRequestTimeTooSkewed: { + ErrCode: ErrRequestTimeTooSkewed, + Code: "RequestTimeTooSkewed", + Description: "The difference between the request time and the server's time is too large.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrSignatureDoesNotMatch: { + ErrCode: ErrSignatureDoesNotMatch, + Code: "SignatureDoesNotMatch", + Description: "The request signature we calculated does not match the signature you provided. Check your key and signing method.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrMethodNotAllowed: { + ErrCode: ErrMethodNotAllowed, + Code: "MethodNotAllowed", + Description: "The specified method is not allowed against this resource.", + HTTPStatusCode: http.StatusMethodNotAllowed, + }, + ErrInvalidPart: { + ErrCode: ErrInvalidPart, + Code: "InvalidPart", + Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidPartOrder: { + ErrCode: ErrInvalidPartOrder, + Code: "InvalidPartOrder", + Description: "The list of parts was not in ascending order. The parts list must be specified in order by part number.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidObjectState: { + ErrCode: ErrInvalidObjectState, + Code: "InvalidObjectState", + Description: "The operation is not valid for the current state of the object.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrAuthorizationHeaderMalformed: { + ErrCode: ErrAuthorizationHeaderMalformed, + Code: "AuthorizationHeaderMalformed", + Description: "The authorization header is malformed; the region is wrong; expecting 'us-east-1'.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMalformedPOSTRequest: { + ErrCode: ErrMalformedPOSTRequest, + Code: "MalformedPOSTRequest", + Description: "The body of your POST request is not well-formed multipart/form-data.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrPOSTFileRequired: { + ErrCode: ErrPOSTFileRequired, + Code: "InvalidArgument", + Description: "POST requires exactly one file upload per request.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrSignatureVersionNotSupported: { + ErrCode: ErrSignatureVersionNotSupported, + Code: "InvalidRequest", + Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBucketNotEmpty: { + ErrCode: ErrBucketNotEmpty, + Code: "BucketNotEmpty", + Description: "The bucket you tried to delete is not empty", + HTTPStatusCode: http.StatusConflict, + }, + ErrBucketAlreadyExists: { + ErrCode: ErrBucketAlreadyExists, + Code: "BucketAlreadyExists", + Description: "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.", + HTTPStatusCode: http.StatusConflict, + }, + ErrAllAccessDisabled: { + ErrCode: ErrAllAccessDisabled, + Code: "AllAccessDisabled", + Description: "All access to this bucket has been disabled.", + HTTPStatusCode: http.StatusForbidden, + }, + ErrMalformedPolicy: { + ErrCode: ErrMalformedPolicy, + Code: "MalformedPolicy", + Description: "Policy has invalid resource.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingFields: { + ErrCode: ErrMissingFields, + Code: "MissingFields", + Description: "Missing fields in request.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingCredTag: { + ErrCode: ErrMissingCredTag, + Code: "InvalidRequest", + Description: "Missing Credential field for this request.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrCredMalformed: { + ErrCode: ErrCredMalformed, + Code: "AuthorizationQueryParametersError", + Description: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"/YYYYMMDD/REGION/SERVICE/aws4_request\".", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMalformedDate: { + ErrCode: ErrMalformedDate, + Code: "MalformedDate", + Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMalformedPresignedDate: { + ErrCode: ErrMalformedPresignedDate, + Code: "AuthorizationQueryParametersError", + Description: "X-Amz-Date must be in the ISO8601 Long Format \"yyyyMMdd'T'HHmmss'Z'\"", + HTTPStatusCode: http.StatusBadRequest, + }, + // FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385. + // right Description: "Error parsing the X-Amz-Credential parameter; incorrect date format \"%s\". This date in the credential must be in the format \"yyyyMMdd\".", + // Need changes to make sure variable messages can be constructed. + ErrMalformedCredentialDate: { + ErrCode: ErrMalformedCredentialDate, + Code: "AuthorizationQueryParametersError", + Description: "Error parsing the X-Amz-Credential parameter; incorrect date format \"%s\". This date in the credential must be in the format \"yyyyMMdd\".", + HTTPStatusCode: http.StatusBadRequest, + }, + // FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385. + // right Description: "Error parsing the X-Amz-Credential parameter; the region 'us-east-' is wrong; expecting 'us-east-1'". + // Need changes to make sure variable messages can be constructed. + ErrMalformedCredentialRegion: { + ErrCode: ErrMalformedCredentialRegion, + Code: "AuthorizationQueryParametersError", + Description: "Error parsing the X-Amz-Credential parameter; the region is wrong;", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidRegion: { + ErrCode: ErrInvalidRegion, + Code: "InvalidRegion", + Description: "Region does not match.", + HTTPStatusCode: http.StatusBadRequest, + }, + // FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385. + // right Description: "Error parsing the X-Amz-Credential parameter; incorrect service \"s4\". This endpoint belongs to \"s3\".". + // Need changes to make sure variable messages can be constructed. + ErrInvalidServiceS3: { + ErrCode: ErrInvalidServiceS3, + Code: "AuthorizationParametersError", + Description: "Error parsing the Credential/X-Amz-Credential parameter; incorrect service. This endpoint belongs to \"s3\".", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidServiceSTS: { + ErrCode: ErrInvalidServiceSTS, + Code: "AuthorizationParametersError", + Description: "Error parsing the Credential parameter; incorrect service. This endpoint belongs to \"sts\".", + HTTPStatusCode: http.StatusBadRequest, + }, + // FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385. + // Description: "Error parsing the X-Amz-Credential parameter; incorrect terminal "aws4_reque". This endpoint uses "aws4_request". + // Need changes to make sure variable messages can be constructed. + ErrInvalidRequestVersion: { + ErrCode: ErrInvalidRequestVersion, + Code: "AuthorizationQueryParametersError", + Description: "Error parsing the X-Amz-Credential parameter; incorrect terminal. This endpoint uses \"aws4_request\".", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingSignTag: { + ErrCode: ErrMissingSignTag, + Code: "AccessDenied", + Description: "Signature header missing Signature field.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingSignHeadersTag: { + ErrCode: ErrMissingSignHeadersTag, + Code: "InvalidArgument", + Description: "Signature header missing SignedHeaders field.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMalformedExpires: { + ErrCode: ErrMalformedExpires, + Code: "AuthorizationQueryParametersError", + Description: "X-Amz-Expires should be a number", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrNegativeExpires: { + ErrCode: ErrNegativeExpires, + Code: "AuthorizationQueryParametersError", + Description: "X-Amz-Expires must be non-negative", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAuthHeaderEmpty: { + ErrCode: ErrAuthHeaderEmpty, + Code: "InvalidArgument", + Description: "Authorization header is invalid -- one and only one ' ' (space) required.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingDateHeader: { + ErrCode: ErrMissingDateHeader, + Code: "AccessDenied", + Description: "AWS authentication requires a valid Date or x-amz-date header", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidQuerySignatureAlgo: { + ErrCode: ErrInvalidQuerySignatureAlgo, + Code: "AuthorizationQueryParametersError", + Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrExpiredPresignRequest: { + ErrCode: ErrExpiredPresignRequest, + Code: "AccessDenied", + Description: "Request has expired", + HTTPStatusCode: http.StatusForbidden, + }, + ErrRequestNotReadyYet: { + ErrCode: ErrRequestNotReadyYet, + Code: "AccessDenied", + Description: "Request is not valid yet", + HTTPStatusCode: http.StatusForbidden, + }, + ErrSlowDown: { + ErrCode: ErrSlowDown, + Code: "SlowDown", + Description: "Please reduce your request", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrInvalidPrefixMarker: { + ErrCode: ErrInvalidPrefixMarker, + Code: "InvalidPrefixMarker", + Description: "Invalid marker prefix combination", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBadRequest: { + ErrCode: ErrBadRequest, + Code: "BadRequest", + Description: "400 BadRequest", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrKeyTooLongError: { + ErrCode: ErrKeyTooLongError, + Code: "KeyTooLongError", + Description: "Your key is too long", + HTTPStatusCode: http.StatusBadRequest, + }, + + // FIXME: Actual XML error response also contains the header which missed in list of signed header parameters. + ErrUnsignedHeaders: { + ErrCode: ErrUnsignedHeaders, + Code: "AccessDenied", + Description: "There were headers present in the request which were not signed", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidQueryParams: { + ErrCode: ErrInvalidQueryParams, + Code: "AuthorizationQueryParametersError", + Description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBucketAlreadyOwnedByYou: { + ErrCode: ErrBucketAlreadyOwnedByYou, + Code: "BucketAlreadyOwnedByYou", + Description: "Your previous request to create the named bucket succeeded and you already own it.", + HTTPStatusCode: http.StatusConflict, + }, + ErrInvalidDuration: { + ErrCode: ErrInvalidDuration, + Code: "InvalidDuration", + Description: "Duration provided in the request is invalid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidBucketObjectLockConfiguration: { + ErrCode: ErrInvalidBucketObjectLockConfiguration, + Code: "InvalidRequest", + Description: "Bucket is missing ObjectLockConfiguration", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBucketTaggingNotFound: { + ErrCode: ErrBucketTaggingNotFound, + Code: "NoSuchTagSet", + Description: "The TagSet does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrObjectLockConfigurationNotFound: { + ErrCode: ErrObjectLockConfigurationNotFound, + Code: "ObjectLockConfigurationNotFoundError", + Description: "Object Lock configuration does not exist for this bucket", + HTTPStatusCode: http.StatusNotFound, + }, + ErrObjectLockConfigurationNotAllowed: { + ErrCode: ErrObjectLockConfigurationNotAllowed, + Code: "InvalidBucketState", + Description: "Object Lock configuration cannot be enabled on existing buckets", + HTTPStatusCode: http.StatusConflict, + }, + ErrNoSuchCORSConfiguration: { + ErrCode: ErrNoSuchCORSConfiguration, + Code: "NoSuchCORSConfiguration", + Description: "The CORS configuration does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchWebsiteConfiguration: { + ErrCode: ErrNoSuchWebsiteConfiguration, + Code: "NoSuchWebsiteConfiguration", + Description: "The specified bucket does not have a website configuration", + HTTPStatusCode: http.StatusNotFound, + }, + ErrReplicationConfigurationNotFoundError: { + ErrCode: ErrReplicationConfigurationNotFoundError, + Code: "ReplicationConfigurationNotFoundError", + Description: "The replication configuration was not found", + HTTPStatusCode: http.StatusNotFound, + }, + ErrNoSuchObjectLockConfiguration: { + ErrCode: ErrNoSuchObjectLockConfiguration, + Code: "NoSuchObjectLockConfiguration", + Description: "The specified object does not have a ObjectLock configuration", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrObjectLocked: { + ErrCode: ErrObjectLocked, + Code: "InvalidRequest", + Description: "Object is WORM protected and cannot be overwritten", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidRetentionDate: { + ErrCode: ErrInvalidRetentionDate, + Code: "InvalidRequest", + Description: "Date must be provided in ISO 8601 format", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrPastObjectLockRetainDate: { + ErrCode: ErrPastObjectLockRetainDate, + Code: "InvalidRequest", + Description: "the retain until date must be in the future", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnknownWORMModeDirective: { + ErrCode: ErrUnknownWORMModeDirective, + Code: "InvalidRequest", + Description: "unknown wormMode directive", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrObjectLockInvalidHeaders: { + ErrCode: ErrObjectLockInvalidHeaders, + Code: "InvalidRequest", + Description: "x-amz-object-lock-retain-until-date and x-amz-object-lock-mode must both be supplied", + HTTPStatusCode: http.StatusBadRequest, + }, + // Bucket notification related errors. + ErrEventNotification: { + ErrCode: ErrEventNotification, + Code: "InvalidArgument", + Description: "A specified event is not supported for notifications.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrARNNotification: { + ErrCode: ErrARNNotification, + Code: "InvalidArgument", + Description: "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrRegionNotification: { + ErrCode: ErrRegionNotification, + Code: "InvalidArgument", + Description: "A specified destination is in a different region than the bucket. You must use a destination that resides in the same region as the bucket.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrOverlappingFilterNotification: { + ErrCode: ErrOverlappingFilterNotification, + Code: "InvalidArgument", + Description: "An object key name filtering rule defined with overlapping prefixes, overlapping suffixes, or overlapping combinations of prefixes and suffixes for the same event types.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrFilterNameInvalid: { + ErrCode: ErrFilterNameInvalid, + Code: "InvalidArgument", + Description: "filter rule name must be either prefix or suffix", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrFilterNamePrefix: { + ErrCode: ErrFilterNamePrefix, + Code: "InvalidArgument", + Description: "Cannot specify more than one prefix rule in a filter.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrFilterNameSuffix: { + ErrCode: ErrFilterNameSuffix, + Code: "InvalidArgument", + Description: "Cannot specify more than one suffix rule in a filter.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrFilterValueInvalid: { + ErrCode: ErrFilterValueInvalid, + Code: "InvalidArgument", + Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrOverlappingConfigs: { + ErrCode: ErrOverlappingConfigs, + Code: "InvalidArgument", + Description: "Configurations overlap. Configurations on the same bucket cannot share a common event type.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedNotification: { + ErrCode: ErrUnsupportedNotification, + Code: "UnsupportedNotification", + Description: "MinIO server does not support Topic or Cloud Function based notifications.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidCopyPartRange: { + ErrCode: ErrInvalidCopyPartRange, + Code: "InvalidArgument", + Description: "The x-amz-copy-source-range value must be of the form bytes=first-last where first and last are the zero-based offsets of the first and last bytes to copy", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidCopyPartRangeSource: { + ErrCode: ErrInvalidCopyPartRangeSource, + Code: "InvalidArgument", + Description: "Range specified is not valid for source object", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMetadataTooLarge: { + ErrCode: ErrMetadataTooLarge, + Code: "InvalidArgument", + Description: "Your metadata headers exceed the maximum allowed metadata size.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTagDirective: { + ErrCode: ErrInvalidTagDirective, + Code: "InvalidArgument", + Description: "Unknown tag directive.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrNotSupported: { + ErrCode: ErrNotSupported, + Code: "BadRequest", + Description: "Not supported by NeoFS S3 Gateway", + HTTPStatusCode: http.StatusNotImplemented, + }, + ErrInvalidEncryptionMethod: { + ErrCode: ErrInvalidEncryptionMethod, + Code: "InvalidRequest", + Description: "The encryption method specified is not supported", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInsecureSSECustomerRequest: { + ErrCode: ErrInsecureSSECustomerRequest, + Code: "InvalidRequest", + Description: "Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrSSEMultipartEncrypted: { + ErrCode: ErrSSEMultipartEncrypted, + Code: "InvalidRequest", + Description: "The multipart upload initiate requested encryption. Subsequent part requests must include the appropriate encryption parameters.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrSSEEncryptedObject: { + ErrCode: ErrSSEEncryptedObject, + Code: "InvalidRequest", + Description: "The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidEncryptionParameters: { + ErrCode: ErrInvalidEncryptionParameters, + Code: "InvalidRequest", + Description: "The encryption parameters are not applicable to this object.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidSSECustomerAlgorithm: { + ErrCode: ErrInvalidSSECustomerAlgorithm, + Code: "InvalidArgument", + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidSSECustomerKey: { + ErrCode: ErrInvalidSSECustomerKey, + Code: "InvalidArgument", + Description: "The secret key was invalid for the specified algorithm.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingSSECustomerKey: { + ErrCode: ErrMissingSSECustomerKey, + Code: "InvalidArgument", + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingSSECustomerKeyMD5: { + ErrCode: ErrMissingSSECustomerKeyMD5, + Code: "InvalidArgument", + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrSSECustomerKeyMD5Mismatch: { + ErrCode: ErrSSECustomerKeyMD5Mismatch, + Code: "InvalidArgument", + Description: "The calculated MD5 hash of the key did not match the hash that was provided.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidSSECustomerParameters: { + ErrCode: ErrInvalidSSECustomerParameters, + Code: "InvalidArgument", + Description: "The provided encryption parameters did not match the ones used originally.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIncompatibleEncryptionMethod: { + ErrCode: ErrIncompatibleEncryptionMethod, + Code: "InvalidArgument", + Description: "Server side encryption specified with both SSE-C and SSE-S3 headers", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrKMSNotConfigured: { + ErrCode: ErrKMSNotConfigured, + Code: "InvalidArgument", + Description: "Server side encryption specified but KMS is not configured", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrKMSAuthFailure: { + ErrCode: ErrKMSAuthFailure, + Code: "InvalidArgument", + Description: "Server side encryption specified but KMS authorization failed", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrNoAccessKey: { + ErrCode: ErrNoAccessKey, + Code: "AccessDenied", + Description: "No AWSAccessKey was presented", + HTTPStatusCode: http.StatusForbidden, + }, + ErrInvalidToken: { + ErrCode: ErrInvalidToken, + Code: "InvalidTokenId", + Description: "The security token included in the request is invalid", + HTTPStatusCode: http.StatusForbidden, + }, + + // S3 extensions. + ErrContentSHA256Mismatch: { + ErrCode: ErrContentSHA256Mismatch, + Code: "XAmzContentSHA256Mismatch", + Description: "The provided 'x-amz-content-sha256' header does not match what was computed.", + HTTPStatusCode: http.StatusBadRequest, + }, + + // MinIO extensions. + ErrStorageFull: { + ErrCode: ErrStorageFull, + Code: "XMinioStorageFull", + Description: "Storage backend has reached its minimum free disk threshold. Please delete a few objects to proceed.", + HTTPStatusCode: http.StatusInsufficientStorage, + }, + ErrParentIsObject: { + ErrCode: ErrParentIsObject, + Code: "XMinioParentIsObject", + Description: "Object-prefix is already an object, please choose a different object-prefix name.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrRequestBodyParse: { + ErrCode: ErrRequestBodyParse, + Code: "XMinioRequestBodyParse", + Description: "The request body failed to parse.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrObjectExistsAsDirectory: { + ErrCode: ErrObjectExistsAsDirectory, + Code: "XMinioObjectExistsAsDirectory", + Description: "Object name already exists as a directory.", + HTTPStatusCode: http.StatusConflict, + }, + ErrInvalidObjectName: { + ErrCode: ErrInvalidObjectName, + Code: "XMinioInvalidObjectName", + Description: "Object name contains unsupported characters.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidObjectNamePrefixSlash: { + ErrCode: ErrInvalidObjectNamePrefixSlash, + Code: "XMinioInvalidObjectName", + Description: "Object name contains a leading slash.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidResourceName: { + ErrCode: ErrInvalidResourceName, + Code: "XMinioInvalidResourceName", + Description: "Resource name contains bad components such as \"..\" or \".\".", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrServerNotInitialized: { + ErrCode: ErrServerNotInitialized, + Code: "XMinioServerNotInitialized", + Description: "Server not initialized, please try again.", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrMalformedJSON: { + ErrCode: ErrMalformedJSON, + Code: "XMinioMalformedJSON", + Description: "The JSON you provided was not well-formed or did not validate against our published format.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminNoSuchUser: { + ErrCode: ErrAdminNoSuchUser, + Code: "XMinioAdminNoSuchUser", + Description: "The specified user does not exist.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrAdminNoSuchGroup: { + ErrCode: ErrAdminNoSuchGroup, + Code: "XMinioAdminNoSuchGroup", + Description: "The specified group does not exist.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrAdminGroupNotEmpty: { + ErrCode: ErrAdminGroupNotEmpty, + Code: "XMinioAdminGroupNotEmpty", + Description: "The specified group is not empty - cannot remove it.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminNoSuchPolicy: { + ErrCode: ErrAdminNoSuchPolicy, + Code: "XMinioAdminNoSuchPolicy", + Description: "The canned policy does not exist.", + HTTPStatusCode: http.StatusNotFound, + }, + ErrAdminInvalidArgument: { + ErrCode: ErrAdminInvalidArgument, + Code: "XMinioAdminInvalidArgument", + Description: "Invalid arguments specified.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminInvalidAccessKey: { + ErrCode: ErrAdminInvalidAccessKey, + Code: "XMinioAdminInvalidAccessKey", + Description: "The access key is invalid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminInvalidSecretKey: { + ErrCode: ErrAdminInvalidSecretKey, + Code: "XMinioAdminInvalidSecretKey", + Description: "The secret key is invalid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminConfigNoQuorum: { + ErrCode: ErrAdminConfigNoQuorum, + Code: "XMinioAdminConfigNoQuorum", + Description: "Configuration update failed because server quorum was not met", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrAdminConfigTooLarge: { + ErrCode: ErrAdminConfigTooLarge, + Code: "XMinioAdminConfigTooLarge", + Description: fmt.Sprintf("Configuration data provided exceeds the allowed maximum of %d bytes", + maxEConfigJSONSize), + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminConfigBadJSON: { + ErrCode: ErrAdminConfigBadJSON, + Code: "XMinioAdminConfigBadJSON", + Description: "JSON configuration provided is of incorrect format", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminConfigDuplicateKeys: { + ErrCode: ErrAdminConfigDuplicateKeys, + Code: "XMinioAdminConfigDuplicateKeys", + Description: "JSON configuration provided has objects with duplicate keys", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminConfigNotificationTargetsFailed: { + ErrCode: ErrAdminConfigNotificationTargetsFailed, + Code: "XMinioAdminNotificationTargetsTestFailed", + Description: "Configuration update failed due an unsuccessful attempt to connect to one or more notification servers", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminProfilerNotEnabled: { + ErrCode: ErrAdminProfilerNotEnabled, + Code: "XMinioAdminProfilerNotEnabled", + Description: "Unable to perform the requested operation because profiling is not enabled", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminCredentialsMismatch: { + ErrCode: ErrAdminCredentialsMismatch, + Code: "XMinioAdminCredentialsMismatch", + Description: "Credentials in config mismatch with server environment variables", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrAdminBucketQuotaExceeded: { + ErrCode: ErrAdminBucketQuotaExceeded, + Code: "XMinioAdminBucketQuotaExceeded", + Description: "Bucket quota exceeded", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAdminNoSuchQuotaConfiguration: { + ErrCode: ErrAdminNoSuchQuotaConfiguration, + Code: "XMinioAdminNoSuchQuotaConfiguration", + Description: "The quota configuration does not exist", + HTTPStatusCode: http.StatusNotFound, + }, + ErrAdminBucketQuotaDisabled: { + ErrCode: ErrAdminBucketQuotaDisabled, + Code: "XMinioAdminBucketQuotaDisabled", + Description: "Quota specified but disk usage crawl is disabled on MinIO server", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInsecureClientRequest: { + ErrCode: ErrInsecureClientRequest, + Code: "XMinioInsecureClientRequest", + Description: "Cannot respond to plain-text request from TLS-encrypted server", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrOperationTimedOut: { + ErrCode: ErrOperationTimedOut, + Code: "RequestTimeout", + Description: "A timeout occurred while trying to lock a resource, please reduce your request rate", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrOperationMaxedOut: { + ErrCode: ErrOperationMaxedOut, + Code: "SlowDown", + Description: "A timeout exceeded while waiting to proceed with the request, please reduce your request rate", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrUnsupportedMetadata: { + ErrCode: ErrUnsupportedMetadata, + Code: "InvalidArgument", + Description: "Your metadata headers are not supported.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrObjectTampered: { + ErrCode: ErrObjectTampered, + Code: "XMinioObjectTampered", + Description: "The requested object was modified and may be compromised", + HTTPStatusCode: http.StatusPartialContent, + }, + ErrMaximumExpires: { + ErrCode: ErrMaximumExpires, + Code: "AuthorizationQueryParametersError", + Description: "X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds", + HTTPStatusCode: http.StatusBadRequest, + }, + + // Generic Invalid-Request error. Should be used for response errors only for unlikely + // corner case errors for which introducing new ErrorCode is not worth it. LogIf() + // should be used to log the error at the source of the error for debugging purposes. + ErrInvalidRequest: { + ErrCode: ErrInvalidRequest, + Code: "InvalidRequest", + Description: "Invalid Request", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealNotImplemented: { + ErrCode: ErrHealNotImplemented, + Code: "XMinioHealNotImplemented", + Description: "This server does not implement heal functionality.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealNoSuchProcess: { + ErrCode: ErrHealNoSuchProcess, + Code: "XMinioHealNoSuchProcess", + Description: "No such heal process is running on the server", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealInvalidClientToken: { + ErrCode: ErrHealInvalidClientToken, + Code: "XMinioHealInvalidClientToken", + Description: "Client token mismatch", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealMissingBucket: { + ErrCode: ErrHealMissingBucket, + Code: "XMinioHealMissingBucket", + Description: "A heal start request with a non-empty object-prefix parameter requires a bucket to be specified.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealAlreadyRunning: { + ErrCode: ErrHealAlreadyRunning, + Code: "XMinioHealAlreadyRunning", + Description: "", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrHealOverlappingPaths: { + ErrCode: ErrHealOverlappingPaths, + Code: "XMinioHealOverlappingPaths", + Description: "", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBackendDown: { + ErrCode: ErrBackendDown, + Code: "XMinioBackendDown", + Description: "Object storage backend is unreachable", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrIncorrectContinuationToken: { + ErrCode: ErrIncorrectContinuationToken, + Code: "InvalidArgument", + Description: "The continuation token provided is incorrect", + HTTPStatusCode: http.StatusBadRequest, + }, + + // S3 Select API Errors + ErrEmptyRequestBody: { + ErrCode: ErrEmptyRequestBody, + Code: "EmptyRequestBody", + Description: "Request body cannot be empty.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedFunction: { + ErrCode: ErrUnsupportedFunction, + Code: "UnsupportedFunction", + Description: "Encountered an unsupported SQL function.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidDataSource: { + ErrCode: ErrInvalidDataSource, + Code: "InvalidDataSource", + Description: "Invalid data source type. Only CSV and JSON are supported at this time.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidExpressionType: { + ErrCode: ErrInvalidExpressionType, + Code: "InvalidExpressionType", + Description: "The ExpressionType is invalid. Only SQL expressions are supported at this time.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrBusy: { + ErrCode: ErrBusy, + Code: "Busy", + Description: "The service is unavailable. Please retry.", + HTTPStatusCode: http.StatusServiceUnavailable, + }, + ErrUnauthorizedAccess: { + ErrCode: ErrUnauthorizedAccess, + Code: "UnauthorizedAccess", + Description: "You are not authorized to perform this operation", + HTTPStatusCode: http.StatusUnauthorized, + }, + ErrExpressionTooLong: { + ErrCode: ErrExpressionTooLong, + Code: "ExpressionTooLong", + Description: "The SQL expression is too long: The maximum byte-length for the SQL expression is 256 KB.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIllegalSQLFunctionArgument: { + ErrCode: ErrIllegalSQLFunctionArgument, + Code: "IllegalSqlFunctionArgument", + Description: "Illegal argument was used in the SQL function.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidKeyPath: { + ErrCode: ErrInvalidKeyPath, + Code: "InvalidKeyPath", + Description: "Key path in the SQL expression is invalid.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidCompressionFormat: { + ErrCode: ErrInvalidCompressionFormat, + Code: "InvalidCompressionFormat", + Description: "The file is not in a supported compression format. Only GZIP is supported at this time.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidFileHeaderInfo: { + ErrCode: ErrInvalidFileHeaderInfo, + Code: "InvalidFileHeaderInfo", + Description: "The FileHeaderInfo is invalid. Only NONE, USE, and IGNORE are supported.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidJSONType: { + ErrCode: ErrInvalidJSONType, + Code: "InvalidJsonType", + Description: "The JsonType is invalid. Only DOCUMENT and LINES are supported at this time.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidQuoteFields: { + ErrCode: ErrInvalidQuoteFields, + Code: "InvalidQuoteFields", + Description: "The QuoteFields is invalid. Only ALWAYS and ASNEEDED are supported.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidRequestParameter: { + ErrCode: ErrInvalidRequestParameter, + Code: "InvalidRequestParameter", + Description: "The value of a parameter in SelectRequest element is invalid. Check the service API documentation and try again.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidDataType: { + ErrCode: ErrInvalidDataType, + Code: "InvalidDataType", + Description: "The SQL expression contains an invalid data type.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTextEncoding: { + ErrCode: ErrInvalidTextEncoding, + Code: "InvalidTextEncoding", + Description: "Invalid encoding type. Only UTF-8 encoding is supported at this time.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTableAlias: { + ErrCode: ErrInvalidTableAlias, + Code: "InvalidTableAlias", + Description: "The SQL expression contains an invalid table alias.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingRequiredParameter: { + ErrCode: ErrMissingRequiredParameter, + Code: "MissingRequiredParameter", + Description: "The SelectRequest entity is missing a required parameter. Check the service documentation and try again.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrObjectSerializationConflict: { + ErrCode: ErrObjectSerializationConflict, + Code: "ObjectSerializationConflict", + Description: "The SelectRequest entity can only contain one of CSV or JSON. Check the service documentation and try again.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedSQLOperation: { + ErrCode: ErrUnsupportedSQLOperation, + Code: "UnsupportedSqlOperation", + Description: "Encountered an unsupported SQL operation.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedSQLStructure: { + ErrCode: ErrUnsupportedSQLStructure, + Code: "UnsupportedSqlStructure", + Description: "Encountered an unsupported SQL structure. Check the SQL Reference.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedSyntax: { + ErrCode: ErrUnsupportedSyntax, + Code: "UnsupportedSyntax", + Description: "Encountered invalid syntax.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrUnsupportedRangeHeader: { + ErrCode: ErrUnsupportedRangeHeader, + Code: "UnsupportedRangeHeader", + Description: "Range header is not supported for this operation.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrLexerInvalidChar: { + ErrCode: ErrLexerInvalidChar, + Code: "LexerInvalidChar", + Description: "The SQL expression contains an invalid character.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrLexerInvalidOperator: { + ErrCode: ErrLexerInvalidOperator, + Code: "LexerInvalidOperator", + Description: "The SQL expression contains an invalid literal.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrLexerInvalidLiteral: { + ErrCode: ErrLexerInvalidLiteral, + Code: "LexerInvalidLiteral", + Description: "The SQL expression contains an invalid operator.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrLexerInvalidIONLiteral: { + ErrCode: ErrLexerInvalidIONLiteral, + Code: "LexerInvalidIONLiteral", + Description: "The SQL expression contains an invalid operator.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedDatePart: { + ErrCode: ErrParseExpectedDatePart, + Code: "ParseExpectedDatePart", + Description: "Did not find the expected date part in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedKeyword: { + ErrCode: ErrParseExpectedKeyword, + Code: "ParseExpectedKeyword", + Description: "Did not find the expected keyword in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedTokenType: { + ErrCode: ErrParseExpectedTokenType, + Code: "ParseExpectedTokenType", + Description: "Did not find the expected token in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpected2TokenTypes: { + ErrCode: ErrParseExpected2TokenTypes, + Code: "ParseExpected2TokenTypes", + Description: "Did not find the expected token in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedNumber: { + ErrCode: ErrParseExpectedNumber, + Code: "ParseExpectedNumber", + Description: "Did not find the expected number in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedRightParenBuiltinFunctionCall: { + ErrCode: ErrParseExpectedRightParenBuiltinFunctionCall, + Code: "ParseExpectedRightParenBuiltinFunctionCall", + Description: "Did not find the expected right parenthesis character in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedTypeName: { + ErrCode: ErrParseExpectedTypeName, + Code: "ParseExpectedTypeName", + Description: "Did not find the expected type name in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedWhenClause: { + ErrCode: ErrParseExpectedWhenClause, + Code: "ParseExpectedWhenClause", + Description: "Did not find the expected WHEN clause in the SQL expression. CASE is not supported.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedToken: { + ErrCode: ErrParseUnsupportedToken, + Code: "ParseUnsupportedToken", + Description: "The SQL expression contains an unsupported token.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedLiteralsGroupBy: { + ErrCode: ErrParseUnsupportedLiteralsGroupBy, + Code: "ParseUnsupportedLiteralsGroupBy", + Description: "The SQL expression contains an unsupported use of GROUP BY.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedMember: { + ErrCode: ErrParseExpectedMember, + Code: "ParseExpectedMember", + Description: "The SQL expression contains an unsupported use of MEMBER.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedSelect: { + ErrCode: ErrParseUnsupportedSelect, + Code: "ParseUnsupportedSelect", + Description: "The SQL expression contains an unsupported use of SELECT.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedCase: { + ErrCode: ErrParseUnsupportedCase, + Code: "ParseUnsupportedCase", + Description: "The SQL expression contains an unsupported use of CASE.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedCaseClause: { + ErrCode: ErrParseUnsupportedCaseClause, + Code: "ParseUnsupportedCaseClause", + Description: "The SQL expression contains an unsupported use of CASE.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedAlias: { + ErrCode: ErrParseUnsupportedAlias, + Code: "ParseUnsupportedAlias", + Description: "The SQL expression contains an unsupported use of ALIAS.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedSyntax: { + ErrCode: ErrParseUnsupportedSyntax, + Code: "ParseUnsupportedSyntax", + Description: "The SQL expression contains unsupported syntax.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnknownOperator: { + ErrCode: ErrParseUnknownOperator, + Code: "ParseUnknownOperator", + Description: "The SQL expression contains an invalid operator.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseMissingIdentAfterAt: { + ErrCode: ErrParseMissingIdentAfterAt, + Code: "ParseMissingIdentAfterAt", + Description: "Did not find the expected identifier after the @ symbol in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnexpectedOperator: { + ErrCode: ErrParseUnexpectedOperator, + Code: "ParseUnexpectedOperator", + Description: "The SQL expression contains an unexpected operator.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnexpectedTerm: { + ErrCode: ErrParseUnexpectedTerm, + Code: "ParseUnexpectedTerm", + Description: "The SQL expression contains an unexpected term.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnexpectedToken: { + ErrCode: ErrParseUnexpectedToken, + Code: "ParseUnexpectedToken", + Description: "The SQL expression contains an unexpected token.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnexpectedKeyword: { + ErrCode: ErrParseUnexpectedKeyword, + Code: "ParseUnexpectedKeyword", + Description: "The SQL expression contains an unexpected keyword.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedExpression: { + ErrCode: ErrParseExpectedExpression, + Code: "ParseExpectedExpression", + Description: "Did not find the expected SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedLeftParenAfterCast: { + ErrCode: ErrParseExpectedLeftParenAfterCast, + Code: "ParseExpectedLeftParenAfterCast", + Description: "Did not find expected the left parenthesis in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedLeftParenValueConstructor: { + ErrCode: ErrParseExpectedLeftParenValueConstructor, + Code: "ParseExpectedLeftParenValueConstructor", + Description: "Did not find expected the left parenthesis in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedLeftParenBuiltinFunctionCall: { + ErrCode: ErrParseExpectedLeftParenBuiltinFunctionCall, + Code: "ParseExpectedLeftParenBuiltinFunctionCall", + Description: "Did not find the expected left parenthesis in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedArgumentDelimiter: { + ErrCode: ErrParseExpectedArgumentDelimiter, + Code: "ParseExpectedArgumentDelimiter", + Description: "Did not find the expected argument delimiter in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseCastArity: { + ErrCode: ErrParseCastArity, + Code: "ParseCastArity", + Description: "The SQL expression CAST has incorrect arity.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseInvalidTypeParam: { + ErrCode: ErrParseInvalidTypeParam, + Code: "ParseInvalidTypeParam", + Description: "The SQL expression contains an invalid parameter value.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseEmptySelect: { + ErrCode: ErrParseEmptySelect, + Code: "ParseEmptySelect", + Description: "The SQL expression contains an empty SELECT.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseSelectMissingFrom: { + ErrCode: ErrParseSelectMissingFrom, + Code: "ParseSelectMissingFrom", + Description: "GROUP is not supported in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedIdentForGroupName: { + ErrCode: ErrParseExpectedIdentForGroupName, + Code: "ParseExpectedIdentForGroupName", + Description: "GROUP is not supported in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedIdentForAlias: { + ErrCode: ErrParseExpectedIdentForAlias, + Code: "ParseExpectedIdentForAlias", + Description: "Did not find the expected identifier for the alias in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseUnsupportedCallWithStar: { + ErrCode: ErrParseUnsupportedCallWithStar, + Code: "ParseUnsupportedCallWithStar", + Description: "Only COUNT with (*) as a parameter is supported in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseNonUnaryAgregateFunctionCall: { + ErrCode: ErrParseNonUnaryAgregateFunctionCall, + Code: "ParseNonUnaryAgregateFunctionCall", + Description: "Only one argument is supported for aggregate functions in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseMalformedJoin: { + ErrCode: ErrParseMalformedJoin, + Code: "ParseMalformedJoin", + Description: "JOIN is not supported in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseExpectedIdentForAt: { + ErrCode: ErrParseExpectedIdentForAt, + Code: "ParseExpectedIdentForAt", + Description: "Did not find the expected identifier for AT name in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseAsteriskIsNotAloneInSelectList: { + ErrCode: ErrParseAsteriskIsNotAloneInSelectList, + Code: "ParseAsteriskIsNotAloneInSelectList", + Description: "Other expressions are not allowed in the SELECT list when '*' is used without dot notation in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseCannotMixSqbAndWildcardInSelectList: { + ErrCode: ErrParseCannotMixSqbAndWildcardInSelectList, + Code: "ParseCannotMixSqbAndWildcardInSelectList", + Description: "Cannot mix [] and * in the same expression in a SELECT list in SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrParseInvalidContextForWildcardInSelectList: { + ErrCode: ErrParseInvalidContextForWildcardInSelectList, + Code: "ParseInvalidContextForWildcardInSelectList", + Description: "Invalid use of * in SELECT list in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIncorrectSQLFunctionArgumentType: { + ErrCode: ErrIncorrectSQLFunctionArgumentType, + Code: "IncorrectSqlFunctionArgumentType", + Description: "Incorrect type of arguments in function call in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrValueParseFailure: { + ErrCode: ErrValueParseFailure, + Code: "ValueParseFailure", + Description: "Time stamp parse failure in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorInvalidArguments: { + ErrCode: ErrEvaluatorInvalidArguments, + Code: "EvaluatorInvalidArguments", + Description: "Incorrect number of arguments in the function call in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrIntegerOverflow: { + ErrCode: ErrIntegerOverflow, + Code: "IntegerOverflow", + Description: "Int overflow or underflow in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrLikeInvalidInputs: { + ErrCode: ErrLikeInvalidInputs, + Code: "LikeInvalidInputs", + Description: "Invalid argument given to the LIKE clause in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrCastFailed: { + ErrCode: ErrCastFailed, + Code: "CastFailed", + Description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidCast: { + ErrCode: ErrInvalidCast, + Code: "InvalidCast", + Description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorInvalidTimestampFormatPattern: { + ErrCode: ErrEvaluatorInvalidTimestampFormatPattern, + Code: "EvaluatorInvalidTimestampFormatPattern", + Description: "Time stamp format pattern requires additional fields in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing: { + ErrCode: ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing, + Code: "EvaluatorInvalidTimestampFormatPatternSymbolForParsing", + Description: "Time stamp format pattern contains a valid format symbol that cannot be applied to time stamp parsing in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorTimestampFormatPatternDuplicateFields: { + ErrCode: ErrEvaluatorTimestampFormatPatternDuplicateFields, + Code: "EvaluatorTimestampFormatPatternDuplicateFields", + Description: "Time stamp format pattern contains multiple format specifiers representing the time stamp field in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch: { + ErrCode: ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch, + Code: "EvaluatorUnterminatedTimestampFormatPatternToken", + Description: "Time stamp format pattern contains unterminated token in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorUnterminatedTimestampFormatPatternToken: { + ErrCode: ErrEvaluatorUnterminatedTimestampFormatPatternToken, + Code: "EvaluatorInvalidTimestampFormatPatternToken", + Description: "Time stamp format pattern contains an invalid token in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorInvalidTimestampFormatPatternToken: { + ErrCode: ErrEvaluatorInvalidTimestampFormatPatternToken, + Code: "EvaluatorInvalidTimestampFormatPatternToken", + Description: "Time stamp format pattern contains an invalid token in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorInvalidTimestampFormatPatternSymbol: { + ErrCode: ErrEvaluatorInvalidTimestampFormatPatternSymbol, + Code: "EvaluatorInvalidTimestampFormatPatternSymbol", + Description: "Time stamp format pattern contains an invalid symbol in the SQL expression.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrEvaluatorBindingDoesNotExist: { + ErrCode: ErrEvaluatorBindingDoesNotExist, + Code: "ErrEvaluatorBindingDoesNotExist", + Description: "A column name or a path provided does not exist in the SQL expression", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrMissingHeaders: { + ErrCode: ErrMissingHeaders, + Code: "MissingHeaders", + Description: "Some headers in the query are missing from the file. Check the file and try again.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidColumnIndex: { + ErrCode: ErrInvalidColumnIndex, + Code: "InvalidColumnIndex", + Description: "The column index is invalid. Please check the service documentation and try again.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidDecompressedSize: { + ErrCode: ErrInvalidDecompressedSize, + Code: "XMinioInvalidDecompressedSize", + Description: "The data provided is unfit for decompression", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrAddUserInvalidArgument: { + ErrCode: ErrAddUserInvalidArgument, + Code: "XMinioInvalidIAMCredentials", + Description: "User is not allowed to be same as admin access key", + HTTPStatusCode: http.StatusConflict, + }, + ErrAdminAccountNotEligible: { + ErrCode: ErrAdminAccountNotEligible, + Code: "XMinioInvalidIAMCredentials", + Description: "The administrator key is not eligible for this operation", + HTTPStatusCode: http.StatusConflict, + }, + ErrServiceAccountNotFound: { + ErrCode: ErrServiceAccountNotFound, + Code: "XMinioInvalidIAMCredentials", + Description: "The specified service account is not found", + HTTPStatusCode: http.StatusNotFound, + }, + ErrPostPolicyConditionInvalidFormat: { + ErrCode: ErrPostPolicyConditionInvalidFormat, + Code: "PostPolicyInvalidKeyName", + Description: "Invalid according to Policy: Policy Condition failed", + HTTPStatusCode: http.StatusForbidden, + }, + // Add your error structure here. +} + // IsS3Error check if the provided error is a specific s3 error. func IsS3Error(err error, code ErrorCode) bool { e, ok := err.(Error) return ok && e.ErrCode == code } +func (e errorCodeMap) toAPIErrWithErr(errCode ErrorCode, err error) Error { + apiErr, ok := e[errCode] + if !ok { + apiErr = e[ErrInternalError] + } + if err != nil { + apiErr.Description = fmt.Sprintf("%s (%s)", apiErr.Description, err) + } + return apiErr +} + +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 { + return apiErr + } + return errorCodes.toAPIErr(ErrInternalError) +} + // ObjectError - error that linked to specific object. type ObjectError struct { Err error diff --git a/api/handler/s3encoder.go b/api/handler/s3encoder.go index abeebd1..3e205a8 100644 --- a/api/handler/s3encoder.go +++ b/api/handler/s3encoder.go @@ -1 +1,111 @@ package handler + +import ( + "strings" +) + +type encoding int + +const ( + encodePathSegment encoding = iota + encodeQueryComponent +) + +const ( + urlEncodingType = "url" + upperhex = "0123456789ABCDEF" +) + +func shouldEscape(c byte) bool { + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '_', '.', '/', '*': + return false + } + return true +} + +// s3URLEncode is based on url.QueryEscape() code, +// while considering some S3 exceptions. +func s3URLEncode(s string, mode encoding) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c) { + if c == ' ' && mode == encodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + var buf [64]byte + var t []byte + + required := len(s) + 2*hexCount + if required <= len(buf) { + t = buf[:required] + } else { + t = make([]byte, required) + } + + if hexCount == 0 { + copy(t, s) + for i := 0; i < len(s); i++ { + if s[i] == ' ' { + t[i] = '+' + } + } + return string(t) + } + + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == encodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c): + t[j] = '%' + t[j+1] = upperhex[c>>4] + t[j+2] = upperhex[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +func s3QueryEncode(name string, encodingType string) (result string) { + if encodingType == "" { + return name + } + encodingType = strings.ToLower(encodingType) + switch encodingType { + case urlEncodingType: + return s3URLEncode(name, encodeQueryComponent) + } + return name +} + +func s3PathEncode(name string, encodingType string) (result string) { + if encodingType == "" { + return name + } + encodingType = strings.ToLower(encodingType) + switch encodingType { + case urlEncodingType: + return s3URLEncode(name, encodePathSegment) + } + return name +} diff --git a/api/minio/api-errors.go b/api/minio/api-errors.go deleted file mode 100644 index c0879ef..0000000 --- a/api/minio/api-errors.go +++ /dev/null @@ -1,2235 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package cmd - -import ( - "context" - "encoding/xml" - "errors" - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/Azure/azure-storage-blob-go/azblob" - "google.golang.org/api/googleapi" - - minio "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/tags" - "github.com/minio/minio/internal/auth" - "github.com/minio/minio/internal/bucket/lifecycle" - "github.com/minio/minio/internal/bucket/replication" - "github.com/minio/minio/internal/config/dns" - "github.com/minio/minio/internal/crypto" - "github.com/minio/minio/internal/logger" - - objectlock "github.com/minio/minio/internal/bucket/object/lock" - "github.com/minio/minio/internal/bucket/versioning" - "github.com/minio/minio/internal/event" - "github.com/minio/minio/internal/hash" - "github.com/minio/pkg/bucket/policy" -) - -// APIError structure -type APIError struct { - Code string - Description string - HTTPStatusCode int -} - -// APIErrorResponse - error response format -type APIErrorResponse struct { - XMLName xml.Name `xml:"Error" json:"-"` - Code string - Message string - Key string `xml:"Key,omitempty" json:"Key,omitempty"` - BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"` - Resource string - Region string `xml:"Region,omitempty" json:"Region,omitempty"` - RequestID string `xml:"RequestId" json:"RequestId"` - HostID string `xml:"HostId" json:"HostId"` -} - -// APIErrorCode type of error status. -type APIErrorCode int - -//go:generate stringer -type=APIErrorCode -trimprefix=Err $GOFILE - -// Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html -const ( - ErrNone APIErrorCode = iota - ErrAccessDenied - ErrBadDigest - ErrEntityTooSmall - ErrEntityTooLarge - ErrPolicyTooLarge - ErrIncompleteBody - ErrInternalError - ErrInvalidAccessKeyID - ErrInvalidBucketName - ErrInvalidDigest - ErrInvalidRange - ErrInvalidRangePartNumber - ErrInvalidCopyPartRange - ErrInvalidCopyPartRangeSource - ErrInvalidMaxKeys - ErrInvalidEncodingMethod - ErrInvalidMaxUploads - ErrInvalidMaxParts - ErrInvalidPartNumberMarker - ErrInvalidPartNumber - ErrInvalidRequestBody - ErrInvalidCopySource - ErrInvalidMetadataDirective - ErrInvalidCopyDest - ErrInvalidPolicyDocument - ErrInvalidObjectState - ErrMalformedXML - ErrMissingContentLength - ErrMissingContentMD5 - ErrMissingRequestBodyError - ErrMissingSecurityHeader - ErrNoSuchBucket - ErrNoSuchBucketPolicy - ErrNoSuchBucketLifecycle - ErrNoSuchLifecycleConfiguration - ErrNoSuchBucketSSEConfig - ErrNoSuchCORSConfiguration - ErrNoSuchWebsiteConfiguration - ErrReplicationConfigurationNotFoundError - ErrRemoteDestinationNotFoundError - ErrReplicationDestinationMissingLock - ErrRemoteTargetNotFoundError - ErrReplicationRemoteConnectionError - ErrReplicationBandwidthLimitError - ErrBucketRemoteIdenticalToSource - ErrBucketRemoteAlreadyExists - ErrBucketRemoteLabelInUse - ErrBucketRemoteArnTypeInvalid - ErrBucketRemoteArnInvalid - ErrBucketRemoteRemoveDisallowed - ErrRemoteTargetNotVersionedError - ErrReplicationSourceNotVersionedError - ErrReplicationNeedsVersioningError - ErrReplicationBucketNeedsVersioningError - ErrReplicationNoMatchingRuleError - ErrObjectRestoreAlreadyInProgress - ErrNoSuchKey - ErrNoSuchUpload - ErrInvalidVersionID - ErrNoSuchVersion - ErrNotImplemented - ErrPreconditionFailed - ErrRequestTimeTooSkewed - ErrSignatureDoesNotMatch - ErrMethodNotAllowed - ErrInvalidPart - ErrInvalidPartOrder - ErrAuthorizationHeaderMalformed - ErrMalformedPOSTRequest - ErrPOSTFileRequired - ErrSignatureVersionNotSupported - ErrBucketNotEmpty - ErrAllAccessDisabled - ErrMalformedPolicy - ErrMissingFields - ErrMissingCredTag - ErrCredMalformed - ErrInvalidRegion - ErrInvalidServiceS3 - ErrInvalidServiceSTS - ErrInvalidRequestVersion - ErrMissingSignTag - ErrMissingSignHeadersTag - ErrMalformedDate - ErrMalformedPresignedDate - ErrMalformedCredentialDate - ErrMalformedCredentialRegion - ErrMalformedExpires - ErrNegativeExpires - ErrAuthHeaderEmpty - ErrExpiredPresignRequest - ErrRequestNotReadyYet - ErrUnsignedHeaders - ErrMissingDateHeader - ErrInvalidQuerySignatureAlgo - ErrInvalidQueryParams - ErrBucketAlreadyOwnedByYou - ErrInvalidDuration - ErrBucketAlreadyExists - ErrMetadataTooLarge - ErrUnsupportedMetadata - ErrMaximumExpires - ErrSlowDown - ErrInvalidPrefixMarker - ErrBadRequest - ErrKeyTooLongError - ErrInvalidBucketObjectLockConfiguration - ErrObjectLockConfigurationNotFound - ErrObjectLockConfigurationNotAllowed - ErrNoSuchObjectLockConfiguration - ErrObjectLocked - ErrInvalidRetentionDate - ErrPastObjectLockRetainDate - ErrUnknownWORMModeDirective - ErrBucketTaggingNotFound - ErrObjectLockInvalidHeaders - ErrInvalidTagDirective - // Add new error codes here. - - // SSE-S3 related API errors - ErrInvalidEncryptionMethod - - // Server-Side-Encryption (with Customer provided key) related API errors. - ErrInsecureSSECustomerRequest - ErrSSEMultipartEncrypted - ErrSSEEncryptedObject - ErrInvalidEncryptionParameters - ErrInvalidSSECustomerAlgorithm - ErrInvalidSSECustomerKey - ErrMissingSSECustomerKey - ErrMissingSSECustomerKeyMD5 - ErrSSECustomerKeyMD5Mismatch - ErrInvalidSSECustomerParameters - ErrIncompatibleEncryptionMethod - ErrKMSNotConfigured - - ErrNoAccessKey - ErrInvalidToken - - // Bucket notification related errors. - ErrEventNotification - ErrARNNotification - ErrRegionNotification - ErrOverlappingFilterNotification - ErrFilterNameInvalid - ErrFilterNamePrefix - ErrFilterNameSuffix - ErrFilterValueInvalid - ErrOverlappingConfigs - ErrUnsupportedNotification - - // S3 extended errors. - ErrContentSHA256Mismatch - - // Add new extended error codes here. - - // MinIO extended errors. - ErrReadQuorum - ErrWriteQuorum - ErrStorageFull - ErrRequestBodyParse - ErrObjectExistsAsDirectory - ErrInvalidObjectName - ErrInvalidObjectNamePrefixSlash - ErrInvalidResourceName - ErrServerNotInitialized - ErrOperationTimedOut - ErrClientDisconnected - ErrOperationMaxedOut - ErrInvalidRequest - ErrTransitionStorageClassNotFoundError - // MinIO storage class error codes - ErrInvalidStorageClass - ErrBackendDown - // Add new extended error codes here. - // Please open a https://github.com/minio/minio/issues before adding - // new error codes here. - - ErrMalformedJSON - ErrAdminNoSuchUser - ErrAdminNoSuchGroup - ErrAdminGroupNotEmpty - ErrAdminNoSuchPolicy - ErrAdminInvalidArgument - ErrAdminInvalidAccessKey - ErrAdminInvalidSecretKey - ErrAdminConfigNoQuorum - ErrAdminConfigTooLarge - ErrAdminConfigBadJSON - ErrAdminConfigDuplicateKeys - ErrAdminCredentialsMismatch - ErrInsecureClientRequest - ErrObjectTampered - // Bucket Quota error codes - ErrAdminBucketQuotaExceeded - ErrAdminNoSuchQuotaConfiguration - - ErrHealNotImplemented - ErrHealNoSuchProcess - ErrHealInvalidClientToken - ErrHealMissingBucket - ErrHealAlreadyRunning - ErrHealOverlappingPaths - ErrIncorrectContinuationToken - - // S3 Select Errors - ErrEmptyRequestBody - ErrUnsupportedFunction - ErrInvalidExpressionType - ErrBusy - ErrUnauthorizedAccess - ErrExpressionTooLong - ErrIllegalSQLFunctionArgument - ErrInvalidKeyPath - ErrInvalidCompressionFormat - ErrInvalidFileHeaderInfo - ErrInvalidJSONType - ErrInvalidQuoteFields - ErrInvalidRequestParameter - ErrInvalidDataType - ErrInvalidTextEncoding - ErrInvalidDataSource - ErrInvalidTableAlias - ErrMissingRequiredParameter - ErrObjectSerializationConflict - ErrUnsupportedSQLOperation - ErrUnsupportedSQLStructure - ErrUnsupportedSyntax - ErrUnsupportedRangeHeader - ErrLexerInvalidChar - ErrLexerInvalidOperator - ErrLexerInvalidLiteral - ErrLexerInvalidIONLiteral - ErrParseExpectedDatePart - ErrParseExpectedKeyword - ErrParseExpectedTokenType - ErrParseExpected2TokenTypes - ErrParseExpectedNumber - ErrParseExpectedRightParenBuiltinFunctionCall - ErrParseExpectedTypeName - ErrParseExpectedWhenClause - ErrParseUnsupportedToken - ErrParseUnsupportedLiteralsGroupBy - ErrParseExpectedMember - ErrParseUnsupportedSelect - ErrParseUnsupportedCase - ErrParseUnsupportedCaseClause - ErrParseUnsupportedAlias - ErrParseUnsupportedSyntax - ErrParseUnknownOperator - ErrParseMissingIdentAfterAt - ErrParseUnexpectedOperator - ErrParseUnexpectedTerm - ErrParseUnexpectedToken - ErrParseUnexpectedKeyword - ErrParseExpectedExpression - ErrParseExpectedLeftParenAfterCast - ErrParseExpectedLeftParenValueConstructor - ErrParseExpectedLeftParenBuiltinFunctionCall - ErrParseExpectedArgumentDelimiter - ErrParseCastArity - ErrParseInvalidTypeParam - ErrParseEmptySelect - ErrParseSelectMissingFrom - ErrParseExpectedIdentForGroupName - ErrParseExpectedIdentForAlias - ErrParseUnsupportedCallWithStar - ErrParseNonUnaryAgregateFunctionCall - ErrParseMalformedJoin - ErrParseExpectedIdentForAt - ErrParseAsteriskIsNotAloneInSelectList - ErrParseCannotMixSqbAndWildcardInSelectList - ErrParseInvalidContextForWildcardInSelectList - ErrIncorrectSQLFunctionArgumentType - ErrValueParseFailure - ErrEvaluatorInvalidArguments - ErrIntegerOverflow - ErrLikeInvalidInputs - ErrCastFailed - ErrInvalidCast - ErrEvaluatorInvalidTimestampFormatPattern - ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing - ErrEvaluatorTimestampFormatPatternDuplicateFields - ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch - ErrEvaluatorUnterminatedTimestampFormatPatternToken - ErrEvaluatorInvalidTimestampFormatPatternToken - ErrEvaluatorInvalidTimestampFormatPatternSymbol - ErrEvaluatorBindingDoesNotExist - ErrMissingHeaders - ErrInvalidColumnIndex - - ErrAdminConfigNotificationTargetsFailed - ErrAdminProfilerNotEnabled - ErrInvalidDecompressedSize - ErrAddUserInvalidArgument - ErrAdminAccountNotEligible - ErrAccountNotEligible - ErrAdminServiceAccountNotFound - ErrPostPolicyConditionInvalidFormat -) - -type errorCodeMap map[APIErrorCode]APIError - -func (e errorCodeMap) ToAPIErrWithErr(errCode APIErrorCode, err error) APIError { - apiErr, ok := e[errCode] - if !ok { - apiErr = e[ErrInternalError] - } - if err != nil { - apiErr.Description = fmt.Sprintf("%s (%s)", apiErr.Description, err) - } - if globalServerRegion != "" { - switch errCode { - case ErrAuthorizationHeaderMalformed: - apiErr.Description = fmt.Sprintf("The authorization header is malformed; the region is wrong; expecting '%s'.", globalServerRegion) - return apiErr - } - } - return apiErr -} - -func (e errorCodeMap) ToAPIErr(errCode APIErrorCode) APIError { - return e.ToAPIErrWithErr(errCode, nil) -} - -// error code to APIError structure, these fields carry respective -// descriptions for all the error responses. -var errorCodes = errorCodeMap{ - ErrInvalidCopyDest: { - Code: "InvalidRequest", - Description: "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidCopySource: { - Code: "InvalidArgument", - Description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidMetadataDirective: { - Code: "InvalidArgument", - Description: "Unknown metadata directive.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidStorageClass: { - Code: "InvalidStorageClass", - Description: "Invalid storage class.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRequestBody: { - Code: "InvalidArgument", - Description: "Body shouldn't be set for this request.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidMaxUploads: { - Code: "InvalidArgument", - Description: "Argument max-uploads must be an integer between 0 and 2147483647", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidMaxKeys: { - Code: "InvalidArgument", - Description: "Argument maxKeys must be an integer between 0 and 2147483647", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidEncodingMethod: { - Code: "InvalidArgument", - Description: "Invalid Encoding Method specified in Request", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidMaxParts: { - Code: "InvalidArgument", - Description: "Argument max-parts must be an integer between 0 and 2147483647", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidPartNumberMarker: { - Code: "InvalidArgument", - Description: "Argument partNumberMarker must be an integer.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidPartNumber: { - Code: "InvalidPartNumber", - Description: "The requested partnumber is not satisfiable", - HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable, - }, - ErrInvalidPolicyDocument: { - Code: "InvalidPolicyDocument", - Description: "The content of the form does not meet the conditions specified in the policy document.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAccessDenied: { - Code: "AccessDenied", - Description: "Access Denied.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrBadDigest: { - Code: "BadDigest", - Description: "The Content-Md5 you specified did not match what we received.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEntityTooSmall: { - Code: "EntityTooSmall", - Description: "Your proposed upload is smaller than the minimum allowed object size.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEntityTooLarge: { - Code: "EntityTooLarge", - Description: "Your proposed upload exceeds the maximum allowed object size.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrPolicyTooLarge: { - Code: "PolicyTooLarge", - Description: "Policy exceeds the maximum allowed document size.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrIncompleteBody: { - Code: "IncompleteBody", - Description: "You did not provide the number of bytes specified by the Content-Length HTTP header.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInternalError: { - Code: "InternalError", - Description: "We encountered an internal error, please try again.", - HTTPStatusCode: http.StatusInternalServerError, - }, - ErrInvalidAccessKeyID: { - Code: "InvalidAccessKeyId", - Description: "The Access Key Id you provided does not exist in our records.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrInvalidBucketName: { - Code: "InvalidBucketName", - Description: "The specified bucket is not valid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidDigest: { - Code: "InvalidDigest", - Description: "The Content-Md5 you specified is not valid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRange: { - Code: "InvalidRange", - Description: "The requested range is not satisfiable", - HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable, - }, - ErrInvalidRangePartNumber: { - Code: "InvalidRequest", - Description: "Cannot specify both Range header and partNumber query parameter", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedXML: { - Code: "MalformedXML", - Description: "The XML you provided was not well-formed or did not validate against our published schema.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingContentLength: { - Code: "MissingContentLength", - Description: "You must provide the Content-Length HTTP header.", - HTTPStatusCode: http.StatusLengthRequired, - }, - ErrMissingContentMD5: { - Code: "MissingContentMD5", - Description: "Missing required header for this request: Content-Md5.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingSecurityHeader: { - Code: "MissingSecurityHeader", - Description: "Your request was missing a required header", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingRequestBodyError: { - Code: "MissingRequestBodyError", - Description: "Request body is empty.", - HTTPStatusCode: http.StatusLengthRequired, - }, - ErrNoSuchBucket: { - Code: "NoSuchBucket", - Description: "The specified bucket does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchBucketPolicy: { - Code: "NoSuchBucketPolicy", - Description: "The bucket policy does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchBucketLifecycle: { - Code: "NoSuchBucketLifecycle", - Description: "The bucket lifecycle configuration does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchLifecycleConfiguration: { - Code: "NoSuchLifecycleConfiguration", - Description: "The lifecycle configuration does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchBucketSSEConfig: { - Code: "ServerSideEncryptionConfigurationNotFoundError", - Description: "The server side encryption configuration was not found", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchKey: { - Code: "NoSuchKey", - Description: "The specified key does not exist.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchUpload: { - Code: "NoSuchUpload", - Description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrInvalidVersionID: { - Code: "InvalidArgument", - Description: "Invalid version id specified", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrNoSuchVersion: { - Code: "NoSuchVersion", - Description: "The specified version does not exist.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNotImplemented: { - Code: "NotImplemented", - Description: "A header you provided implies functionality that is not implemented", - HTTPStatusCode: http.StatusNotImplemented, - }, - ErrPreconditionFailed: { - Code: "PreconditionFailed", - Description: "At least one of the pre-conditions you specified did not hold", - HTTPStatusCode: http.StatusPreconditionFailed, - }, - ErrRequestTimeTooSkewed: { - Code: "RequestTimeTooSkewed", - Description: "The difference between the request time and the server's time is too large.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrSignatureDoesNotMatch: { - Code: "SignatureDoesNotMatch", - Description: "The request signature we calculated does not match the signature you provided. Check your key and signing method.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrMethodNotAllowed: { - Code: "MethodNotAllowed", - Description: "The specified method is not allowed against this resource.", - HTTPStatusCode: http.StatusMethodNotAllowed, - }, - ErrInvalidPart: { - Code: "InvalidPart", - Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidPartOrder: { - Code: "InvalidPartOrder", - Description: "The list of parts was not in ascending order. The parts list must be specified in order by part number.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidObjectState: { - Code: "InvalidObjectState", - Description: "The operation is not valid for the current state of the object.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrAuthorizationHeaderMalformed: { - Code: "AuthorizationHeaderMalformed", - Description: "The authorization header is malformed; the region is wrong; expecting 'us-east-1'.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedPOSTRequest: { - Code: "MalformedPOSTRequest", - Description: "The body of your POST request is not well-formed multipart/form-data.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrPOSTFileRequired: { - Code: "InvalidArgument", - Description: "POST requires exactly one file upload per request.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrSignatureVersionNotSupported: { - Code: "InvalidRequest", - Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketNotEmpty: { - Code: "BucketNotEmpty", - Description: "The bucket you tried to delete is not empty", - HTTPStatusCode: http.StatusConflict, - }, - ErrBucketAlreadyExists: { - Code: "BucketAlreadyExists", - Description: "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.", - HTTPStatusCode: http.StatusConflict, - }, - ErrAllAccessDisabled: { - Code: "AllAccessDisabled", - Description: "All access to this bucket has been disabled.", - HTTPStatusCode: http.StatusForbidden, - }, - ErrMalformedPolicy: { - Code: "MalformedPolicy", - Description: "Policy has invalid resource.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingFields: { - Code: "MissingFields", - Description: "Missing fields in request.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingCredTag: { - Code: "InvalidRequest", - Description: "Missing Credential field for this request.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrCredMalformed: { - Code: "AuthorizationQueryParametersError", - Description: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"/YYYYMMDD/REGION/SERVICE/aws4_request\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedDate: { - Code: "MalformedDate", - Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedPresignedDate: { - Code: "AuthorizationQueryParametersError", - Description: "X-Amz-Date must be in the ISO8601 Long Format \"yyyyMMdd'T'HHmmss'Z'\"", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedCredentialDate: { - Code: "AuthorizationQueryParametersError", - Description: "Error parsing the X-Amz-Credential parameter; incorrect date format. This date in the credential must be in the format \"yyyyMMdd\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRegion: { - Code: "InvalidRegion", - Description: "Region does not match.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidServiceS3: { - Code: "AuthorizationParametersError", - Description: "Error parsing the Credential/X-Amz-Credential parameter; incorrect service. This endpoint belongs to \"s3\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidServiceSTS: { - Code: "AuthorizationParametersError", - Description: "Error parsing the Credential parameter; incorrect service. This endpoint belongs to \"sts\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRequestVersion: { - Code: "AuthorizationQueryParametersError", - Description: "Error parsing the X-Amz-Credential parameter; incorrect terminal. This endpoint uses \"aws4_request\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingSignTag: { - Code: "AccessDenied", - Description: "Signature header missing Signature field.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingSignHeadersTag: { - Code: "InvalidArgument", - Description: "Signature header missing SignedHeaders field.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMalformedExpires: { - Code: "AuthorizationQueryParametersError", - Description: "X-Amz-Expires should be a number", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrNegativeExpires: { - Code: "AuthorizationQueryParametersError", - Description: "X-Amz-Expires must be non-negative", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAuthHeaderEmpty: { - Code: "InvalidArgument", - Description: "Authorization header is invalid -- one and only one ' ' (space) required.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingDateHeader: { - Code: "AccessDenied", - Description: "AWS authentication requires a valid Date or x-amz-date header", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidQuerySignatureAlgo: { - Code: "AuthorizationQueryParametersError", - Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrExpiredPresignRequest: { - Code: "AccessDenied", - Description: "Request has expired", - HTTPStatusCode: http.StatusForbidden, - }, - ErrRequestNotReadyYet: { - Code: "AccessDenied", - Description: "Request is not valid yet", - HTTPStatusCode: http.StatusForbidden, - }, - ErrSlowDown: { - Code: "SlowDown", - Description: "Resource requested is unreadable, please reduce your request rate", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrInvalidPrefixMarker: { - Code: "InvalidPrefixMarker", - Description: "Invalid marker prefix combination", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBadRequest: { - Code: "BadRequest", - Description: "400 BadRequest", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrKeyTooLongError: { - Code: "KeyTooLongError", - Description: "Your key is too long", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsignedHeaders: { - Code: "AccessDenied", - Description: "There were headers present in the request which were not signed", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidQueryParams: { - Code: "AuthorizationQueryParametersError", - Description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketAlreadyOwnedByYou: { - Code: "BucketAlreadyOwnedByYou", - Description: "Your previous request to create the named bucket succeeded and you already own it.", - HTTPStatusCode: http.StatusConflict, - }, - ErrInvalidDuration: { - Code: "InvalidDuration", - Description: "Duration provided in the request is invalid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidBucketObjectLockConfiguration: { - Code: "InvalidRequest", - Description: "Bucket is missing ObjectLockConfiguration", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketTaggingNotFound: { - Code: "NoSuchTagSet", - Description: "The TagSet does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrObjectLockConfigurationNotFound: { - Code: "ObjectLockConfigurationNotFoundError", - Description: "Object Lock configuration does not exist for this bucket", - HTTPStatusCode: http.StatusNotFound, - }, - ErrObjectLockConfigurationNotAllowed: { - Code: "InvalidBucketState", - Description: "Object Lock configuration cannot be enabled on existing buckets", - HTTPStatusCode: http.StatusConflict, - }, - ErrNoSuchCORSConfiguration: { - Code: "NoSuchCORSConfiguration", - Description: "The CORS configuration does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrNoSuchWebsiteConfiguration: { - Code: "NoSuchWebsiteConfiguration", - Description: "The specified bucket does not have a website configuration", - HTTPStatusCode: http.StatusNotFound, - }, - ErrReplicationConfigurationNotFoundError: { - Code: "ReplicationConfigurationNotFoundError", - Description: "The replication configuration was not found", - HTTPStatusCode: http.StatusNotFound, - }, - ErrRemoteDestinationNotFoundError: { - Code: "RemoteDestinationNotFoundError", - Description: "The remote destination bucket does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrReplicationDestinationMissingLock: { - Code: "ReplicationDestinationMissingLockError", - Description: "The replication destination bucket does not have object locking enabled", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrRemoteTargetNotFoundError: { - Code: "XMinioAdminRemoteTargetNotFoundError", - Description: "The remote target does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrReplicationRemoteConnectionError: { - Code: "XMinioAdminReplicationRemoteConnectionError", - Description: "Remote service connection error - please check remote service credentials and target bucket", - HTTPStatusCode: http.StatusNotFound, - }, - ErrReplicationBandwidthLimitError: { - Code: "XMinioAdminReplicationBandwidthLimitError", - Description: "Bandwidth limit for remote target must be atleast 100MBps", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrReplicationNoMatchingRuleError: { - Code: "XMinioReplicationNoMatchingRule", - Description: "No matching replication rule found for this object prefix", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteIdenticalToSource: { - Code: "XMinioAdminRemoteIdenticalToSource", - Description: "The remote target cannot be identical to source", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteAlreadyExists: { - Code: "XMinioAdminBucketRemoteAlreadyExists", - Description: "The remote target already exists", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteLabelInUse: { - Code: "XMinioAdminBucketRemoteLabelInUse", - Description: "The remote target with this label already exists", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteRemoveDisallowed: { - Code: "XMinioAdminRemoteRemoveDisallowed", - Description: "This ARN is in use by an existing configuration", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteArnTypeInvalid: { - Code: "XMinioAdminRemoteARNTypeInvalid", - Description: "The bucket remote ARN type is not valid", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBucketRemoteArnInvalid: { - Code: "XMinioAdminRemoteArnInvalid", - Description: "The bucket remote ARN does not have correct format", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrRemoteTargetNotVersionedError: { - Code: "RemoteTargetNotVersionedError", - Description: "The remote target does not have versioning enabled", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrReplicationSourceNotVersionedError: { - Code: "ReplicationSourceNotVersionedError", - Description: "The replication source does not have versioning enabled", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrReplicationNeedsVersioningError: { - Code: "InvalidRequest", - Description: "Versioning must be 'Enabled' on the bucket to apply a replication configuration", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrReplicationBucketNeedsVersioningError: { - Code: "InvalidRequest", - Description: "Versioning must be 'Enabled' on the bucket to add a replication target", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrNoSuchObjectLockConfiguration: { - Code: "NoSuchObjectLockConfiguration", - Description: "The specified object does not have a ObjectLock configuration", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectLocked: { - Code: "InvalidRequest", - Description: "Object is WORM protected and cannot be overwritten", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRetentionDate: { - Code: "InvalidRequest", - Description: "Date must be provided in ISO 8601 format", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrPastObjectLockRetainDate: { - Code: "InvalidRequest", - Description: "the retain until date must be in the future", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnknownWORMModeDirective: { - Code: "InvalidRequest", - Description: "unknown wormMode directive", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectLockInvalidHeaders: { - Code: "InvalidRequest", - Description: "x-amz-object-lock-retain-until-date and x-amz-object-lock-mode must both be supplied", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectRestoreAlreadyInProgress: { - Code: "RestoreAlreadyInProgress", - Description: "Object restore is already in progress", - HTTPStatusCode: http.StatusConflict, - }, - ErrTransitionStorageClassNotFoundError: { - Code: "TransitionStorageClassNotFoundError", - Description: "The transition storage class was not found", - HTTPStatusCode: http.StatusNotFound, - }, - - /// Bucket notification related errors. - ErrEventNotification: { - Code: "InvalidArgument", - Description: "A specified event is not supported for notifications.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrARNNotification: { - Code: "InvalidArgument", - Description: "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrRegionNotification: { - Code: "InvalidArgument", - Description: "A specified destination is in a different region than the bucket. You must use a destination that resides in the same region as the bucket.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrOverlappingFilterNotification: { - Code: "InvalidArgument", - Description: "An object key name filtering rule defined with overlapping prefixes, overlapping suffixes, or overlapping combinations of prefixes and suffixes for the same event types.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrFilterNameInvalid: { - Code: "InvalidArgument", - Description: "filter rule name must be either prefix or suffix", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrFilterNamePrefix: { - Code: "InvalidArgument", - Description: "Cannot specify more than one prefix rule in a filter.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrFilterNameSuffix: { - Code: "InvalidArgument", - Description: "Cannot specify more than one suffix rule in a filter.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrFilterValueInvalid: { - Code: "InvalidArgument", - Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrOverlappingConfigs: { - Code: "InvalidArgument", - Description: "Configurations overlap. Configurations on the same bucket cannot share a common event type.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedNotification: { - Code: "UnsupportedNotification", - Description: "MinIO server does not support Topic or Cloud Function based notifications.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidCopyPartRange: { - Code: "InvalidArgument", - Description: "The x-amz-copy-source-range value must be of the form bytes=first-last where first and last are the zero-based offsets of the first and last bytes to copy", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidCopyPartRangeSource: { - Code: "InvalidArgument", - Description: "Range specified is not valid for source object", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMetadataTooLarge: { - Code: "MetadataTooLarge", - Description: "Your metadata headers exceed the maximum allowed metadata size.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidTagDirective: { - Code: "InvalidArgument", - Description: "Unknown tag directive.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidEncryptionMethod: { - Code: "InvalidRequest", - Description: "The encryption method specified is not supported", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInsecureSSECustomerRequest: { - Code: "InvalidRequest", - Description: "Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrSSEMultipartEncrypted: { - Code: "InvalidRequest", - Description: "The multipart upload initiate requested encryption. Subsequent part requests must include the appropriate encryption parameters.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrSSEEncryptedObject: { - Code: "InvalidRequest", - Description: "The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidEncryptionParameters: { - Code: "InvalidRequest", - Description: "The encryption parameters are not applicable to this object.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidSSECustomerAlgorithm: { - Code: "InvalidArgument", - Description: "Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidSSECustomerKey: { - Code: "InvalidArgument", - Description: "The secret key was invalid for the specified algorithm.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingSSECustomerKey: { - Code: "InvalidArgument", - Description: "Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingSSECustomerKeyMD5: { - Code: "InvalidArgument", - Description: "Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrSSECustomerKeyMD5Mismatch: { - Code: "InvalidArgument", - Description: "The calculated MD5 hash of the key did not match the hash that was provided.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidSSECustomerParameters: { - Code: "InvalidArgument", - Description: "The provided encryption parameters did not match the ones used originally.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrIncompatibleEncryptionMethod: { - Code: "InvalidArgument", - Description: "Server side encryption specified with both SSE-C and SSE-S3 headers", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrKMSNotConfigured: { - Code: "NotImplemented", - Description: "Server side encryption specified but KMS is not configured", - HTTPStatusCode: http.StatusNotImplemented, - }, - ErrNoAccessKey: { - Code: "AccessDenied", - Description: "No AWSAccessKey was presented", - HTTPStatusCode: http.StatusForbidden, - }, - ErrInvalidToken: { - Code: "InvalidTokenId", - Description: "The security token included in the request is invalid", - HTTPStatusCode: http.StatusForbidden, - }, - - /// S3 extensions. - ErrContentSHA256Mismatch: { - Code: "XAmzContentSHA256Mismatch", - Description: "The provided 'x-amz-content-sha256' header does not match what was computed.", - HTTPStatusCode: http.StatusBadRequest, - }, - - /// MinIO extensions. - ErrStorageFull: { - Code: "XMinioStorageFull", - Description: "Storage backend has reached its minimum free disk threshold. Please delete a few objects to proceed.", - HTTPStatusCode: http.StatusInsufficientStorage, - }, - ErrRequestBodyParse: { - Code: "XMinioRequestBodyParse", - Description: "The request body failed to parse.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectExistsAsDirectory: { - Code: "XMinioObjectExistsAsDirectory", - Description: "Object name already exists as a directory.", - HTTPStatusCode: http.StatusConflict, - }, - ErrInvalidObjectName: { - Code: "XMinioInvalidObjectName", - Description: "Object name contains unsupported characters.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidObjectNamePrefixSlash: { - Code: "XMinioInvalidObjectName", - Description: "Object name contains a leading slash.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidResourceName: { - Code: "XMinioInvalidResourceName", - Description: "Resource name contains bad components such as \"..\" or \".\".", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrServerNotInitialized: { - Code: "XMinioServerNotInitialized", - Description: "Server not initialized, please try again.", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrMalformedJSON: { - Code: "XMinioMalformedJSON", - Description: "The JSON you provided was not well-formed or did not validate against our published format.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminNoSuchUser: { - Code: "XMinioAdminNoSuchUser", - Description: "The specified user does not exist.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrAdminNoSuchGroup: { - Code: "XMinioAdminNoSuchGroup", - Description: "The specified group does not exist.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrAdminGroupNotEmpty: { - Code: "XMinioAdminGroupNotEmpty", - Description: "The specified group is not empty - cannot remove it.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminNoSuchPolicy: { - Code: "XMinioAdminNoSuchPolicy", - Description: "The canned policy does not exist.", - HTTPStatusCode: http.StatusNotFound, - }, - ErrAdminInvalidArgument: { - Code: "XMinioAdminInvalidArgument", - Description: "Invalid arguments specified.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminInvalidAccessKey: { - Code: "XMinioAdminInvalidAccessKey", - Description: "The access key is invalid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminInvalidSecretKey: { - Code: "XMinioAdminInvalidSecretKey", - Description: "The secret key is invalid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminConfigNoQuorum: { - Code: "XMinioAdminConfigNoQuorum", - Description: "Configuration update failed because server quorum was not met", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrAdminConfigTooLarge: { - Code: "XMinioAdminConfigTooLarge", - Description: fmt.Sprintf("Configuration data provided exceeds the allowed maximum of %d bytes", - maxEConfigJSONSize), - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminConfigBadJSON: { - Code: "XMinioAdminConfigBadJSON", - Description: "JSON configuration provided is of incorrect format", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminConfigDuplicateKeys: { - Code: "XMinioAdminConfigDuplicateKeys", - Description: "JSON configuration provided has objects with duplicate keys", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminConfigNotificationTargetsFailed: { - Code: "XMinioAdminNotificationTargetsTestFailed", - Description: "Configuration update failed due an unsuccessful attempt to connect to one or more notification servers", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminProfilerNotEnabled: { - Code: "XMinioAdminProfilerNotEnabled", - Description: "Unable to perform the requested operation because profiling is not enabled", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminCredentialsMismatch: { - Code: "XMinioAdminCredentialsMismatch", - Description: "Credentials in config mismatch with server environment variables", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrAdminBucketQuotaExceeded: { - Code: "XMinioAdminBucketQuotaExceeded", - Description: "Bucket quota exceeded", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAdminNoSuchQuotaConfiguration: { - Code: "XMinioAdminNoSuchQuotaConfiguration", - Description: "The quota configuration does not exist", - HTTPStatusCode: http.StatusNotFound, - }, - ErrInsecureClientRequest: { - Code: "XMinioInsecureClientRequest", - Description: "Cannot respond to plain-text request from TLS-encrypted server", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrOperationTimedOut: { - Code: "RequestTimeout", - Description: "A timeout occurred while trying to lock a resource, please reduce your request rate", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrClientDisconnected: { - Code: "ClientDisconnected", - Description: "Client disconnected before response was ready", - HTTPStatusCode: 499, // No official code, use nginx value. - }, - ErrOperationMaxedOut: { - Code: "SlowDown", - Description: "A timeout exceeded while waiting to proceed with the request, please reduce your request rate", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrUnsupportedMetadata: { - Code: "InvalidArgument", - Description: "Your metadata headers are not supported.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectTampered: { - Code: "XMinioObjectTampered", - Description: errObjectTampered.Error(), - HTTPStatusCode: http.StatusPartialContent, - }, - ErrMaximumExpires: { - Code: "AuthorizationQueryParametersError", - Description: "X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds", - HTTPStatusCode: http.StatusBadRequest, - }, - - // Generic Invalid-Request error. Should be used for response errors only for unlikely - // corner case errors for which introducing new APIErrorCode is not worth it. LogIf() - // should be used to log the error at the source of the error for debugging purposes. - ErrInvalidRequest: { - Code: "InvalidRequest", - Description: "Invalid Request", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealNotImplemented: { - Code: "XMinioHealNotImplemented", - Description: "This server does not implement heal functionality.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealNoSuchProcess: { - Code: "XMinioHealNoSuchProcess", - Description: "No such heal process is running on the server", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealInvalidClientToken: { - Code: "XMinioHealInvalidClientToken", - Description: "Client token mismatch", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealMissingBucket: { - Code: "XMinioHealMissingBucket", - Description: "A heal start request with a non-empty object-prefix parameter requires a bucket to be specified.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealAlreadyRunning: { - Code: "XMinioHealAlreadyRunning", - Description: "", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrHealOverlappingPaths: { - Code: "XMinioHealOverlappingPaths", - Description: "", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBackendDown: { - Code: "XMinioBackendDown", - Description: "Object storage backend is unreachable", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrIncorrectContinuationToken: { - Code: "InvalidArgument", - Description: "The continuation token provided is incorrect", - HTTPStatusCode: http.StatusBadRequest, - }, - //S3 Select API Errors - ErrEmptyRequestBody: { - Code: "EmptyRequestBody", - Description: "Request body cannot be empty.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedFunction: { - Code: "UnsupportedFunction", - Description: "Encountered an unsupported SQL function.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidDataSource: { - Code: "InvalidDataSource", - Description: "Invalid data source type. Only CSV and JSON are supported at this time.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidExpressionType: { - Code: "InvalidExpressionType", - Description: "The ExpressionType is invalid. Only SQL expressions are supported at this time.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrBusy: { - Code: "Busy", - Description: "The service is unavailable. Please retry.", - HTTPStatusCode: http.StatusServiceUnavailable, - }, - ErrUnauthorizedAccess: { - Code: "UnauthorizedAccess", - Description: "You are not authorized to perform this operation", - HTTPStatusCode: http.StatusUnauthorized, - }, - ErrExpressionTooLong: { - Code: "ExpressionTooLong", - Description: "The SQL expression is too long: The maximum byte-length for the SQL expression is 256 KB.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrIllegalSQLFunctionArgument: { - Code: "IllegalSqlFunctionArgument", - Description: "Illegal argument was used in the SQL function.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidKeyPath: { - Code: "InvalidKeyPath", - Description: "Key path in the SQL expression is invalid.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidCompressionFormat: { - Code: "InvalidCompressionFormat", - Description: "The file is not in a supported compression format. Only GZIP is supported at this time.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidFileHeaderInfo: { - Code: "InvalidFileHeaderInfo", - Description: "The FileHeaderInfo is invalid. Only NONE, USE, and IGNORE are supported.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidJSONType: { - Code: "InvalidJsonType", - Description: "The JsonType is invalid. Only DOCUMENT and LINES are supported at this time.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidQuoteFields: { - Code: "InvalidQuoteFields", - Description: "The QuoteFields is invalid. Only ALWAYS and ASNEEDED are supported.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidRequestParameter: { - Code: "InvalidRequestParameter", - Description: "The value of a parameter in SelectRequest element is invalid. Check the service API documentation and try again.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidDataType: { - Code: "InvalidDataType", - Description: "The SQL expression contains an invalid data type.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidTextEncoding: { - Code: "InvalidTextEncoding", - Description: "Invalid encoding type. Only UTF-8 encoding is supported at this time.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidTableAlias: { - Code: "InvalidTableAlias", - Description: "The SQL expression contains an invalid table alias.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingRequiredParameter: { - Code: "MissingRequiredParameter", - Description: "The SelectRequest entity is missing a required parameter. Check the service documentation and try again.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrObjectSerializationConflict: { - Code: "ObjectSerializationConflict", - Description: "The SelectRequest entity can only contain one of CSV or JSON. Check the service documentation and try again.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedSQLOperation: { - Code: "UnsupportedSqlOperation", - Description: "Encountered an unsupported SQL operation.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedSQLStructure: { - Code: "UnsupportedSqlStructure", - Description: "Encountered an unsupported SQL structure. Check the SQL Reference.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedSyntax: { - Code: "UnsupportedSyntax", - Description: "Encountered invalid syntax.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrUnsupportedRangeHeader: { - Code: "UnsupportedRangeHeader", - Description: "Range header is not supported for this operation.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrLexerInvalidChar: { - Code: "LexerInvalidChar", - Description: "The SQL expression contains an invalid character.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrLexerInvalidOperator: { - Code: "LexerInvalidOperator", - Description: "The SQL expression contains an invalid literal.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrLexerInvalidLiteral: { - Code: "LexerInvalidLiteral", - Description: "The SQL expression contains an invalid operator.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrLexerInvalidIONLiteral: { - Code: "LexerInvalidIONLiteral", - Description: "The SQL expression contains an invalid operator.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedDatePart: { - Code: "ParseExpectedDatePart", - Description: "Did not find the expected date part in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedKeyword: { - Code: "ParseExpectedKeyword", - Description: "Did not find the expected keyword in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedTokenType: { - Code: "ParseExpectedTokenType", - Description: "Did not find the expected token in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpected2TokenTypes: { - Code: "ParseExpected2TokenTypes", - Description: "Did not find the expected token in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedNumber: { - Code: "ParseExpectedNumber", - Description: "Did not find the expected number in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedRightParenBuiltinFunctionCall: { - Code: "ParseExpectedRightParenBuiltinFunctionCall", - Description: "Did not find the expected right parenthesis character in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedTypeName: { - Code: "ParseExpectedTypeName", - Description: "Did not find the expected type name in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedWhenClause: { - Code: "ParseExpectedWhenClause", - Description: "Did not find the expected WHEN clause in the SQL expression. CASE is not supported.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedToken: { - Code: "ParseUnsupportedToken", - Description: "The SQL expression contains an unsupported token.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedLiteralsGroupBy: { - Code: "ParseUnsupportedLiteralsGroupBy", - Description: "The SQL expression contains an unsupported use of GROUP BY.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedMember: { - Code: "ParseExpectedMember", - Description: "The SQL expression contains an unsupported use of MEMBER.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedSelect: { - Code: "ParseUnsupportedSelect", - Description: "The SQL expression contains an unsupported use of SELECT.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedCase: { - Code: "ParseUnsupportedCase", - Description: "The SQL expression contains an unsupported use of CASE.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedCaseClause: { - Code: "ParseUnsupportedCaseClause", - Description: "The SQL expression contains an unsupported use of CASE.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedAlias: { - Code: "ParseUnsupportedAlias", - Description: "The SQL expression contains an unsupported use of ALIAS.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedSyntax: { - Code: "ParseUnsupportedSyntax", - Description: "The SQL expression contains unsupported syntax.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnknownOperator: { - Code: "ParseUnknownOperator", - Description: "The SQL expression contains an invalid operator.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseMissingIdentAfterAt: { - Code: "ParseMissingIdentAfterAt", - Description: "Did not find the expected identifier after the @ symbol in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnexpectedOperator: { - Code: "ParseUnexpectedOperator", - Description: "The SQL expression contains an unexpected operator.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnexpectedTerm: { - Code: "ParseUnexpectedTerm", - Description: "The SQL expression contains an unexpected term.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnexpectedToken: { - Code: "ParseUnexpectedToken", - Description: "The SQL expression contains an unexpected token.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnexpectedKeyword: { - Code: "ParseUnexpectedKeyword", - Description: "The SQL expression contains an unexpected keyword.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedExpression: { - Code: "ParseExpectedExpression", - Description: "Did not find the expected SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedLeftParenAfterCast: { - Code: "ParseExpectedLeftParenAfterCast", - Description: "Did not find expected the left parenthesis in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedLeftParenValueConstructor: { - Code: "ParseExpectedLeftParenValueConstructor", - Description: "Did not find expected the left parenthesis in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedLeftParenBuiltinFunctionCall: { - Code: "ParseExpectedLeftParenBuiltinFunctionCall", - Description: "Did not find the expected left parenthesis in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedArgumentDelimiter: { - Code: "ParseExpectedArgumentDelimiter", - Description: "Did not find the expected argument delimiter in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseCastArity: { - Code: "ParseCastArity", - Description: "The SQL expression CAST has incorrect arity.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseInvalidTypeParam: { - Code: "ParseInvalidTypeParam", - Description: "The SQL expression contains an invalid parameter value.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseEmptySelect: { - Code: "ParseEmptySelect", - Description: "The SQL expression contains an empty SELECT.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseSelectMissingFrom: { - Code: "ParseSelectMissingFrom", - Description: "GROUP is not supported in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedIdentForGroupName: { - Code: "ParseExpectedIdentForGroupName", - Description: "GROUP is not supported in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedIdentForAlias: { - Code: "ParseExpectedIdentForAlias", - Description: "Did not find the expected identifier for the alias in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseUnsupportedCallWithStar: { - Code: "ParseUnsupportedCallWithStar", - Description: "Only COUNT with (*) as a parameter is supported in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseNonUnaryAgregateFunctionCall: { - Code: "ParseNonUnaryAgregateFunctionCall", - Description: "Only one argument is supported for aggregate functions in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseMalformedJoin: { - Code: "ParseMalformedJoin", - Description: "JOIN is not supported in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseExpectedIdentForAt: { - Code: "ParseExpectedIdentForAt", - Description: "Did not find the expected identifier for AT name in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseAsteriskIsNotAloneInSelectList: { - Code: "ParseAsteriskIsNotAloneInSelectList", - Description: "Other expressions are not allowed in the SELECT list when '*' is used without dot notation in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseCannotMixSqbAndWildcardInSelectList: { - Code: "ParseCannotMixSqbAndWildcardInSelectList", - Description: "Cannot mix [] and * in the same expression in a SELECT list in SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrParseInvalidContextForWildcardInSelectList: { - Code: "ParseInvalidContextForWildcardInSelectList", - Description: "Invalid use of * in SELECT list in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrIncorrectSQLFunctionArgumentType: { - Code: "IncorrectSqlFunctionArgumentType", - Description: "Incorrect type of arguments in function call in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrValueParseFailure: { - Code: "ValueParseFailure", - Description: "Time stamp parse failure in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorInvalidArguments: { - Code: "EvaluatorInvalidArguments", - Description: "Incorrect number of arguments in the function call in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrIntegerOverflow: { - Code: "IntegerOverflow", - Description: "Int overflow or underflow in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrLikeInvalidInputs: { - Code: "LikeInvalidInputs", - Description: "Invalid argument given to the LIKE clause in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrCastFailed: { - Code: "CastFailed", - Description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidCast: { - Code: "InvalidCast", - Description: "Attempt to convert from one data type to another using CAST failed in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorInvalidTimestampFormatPattern: { - Code: "EvaluatorInvalidTimestampFormatPattern", - Description: "Time stamp format pattern requires additional fields in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing: { - Code: "EvaluatorInvalidTimestampFormatPatternSymbolForParsing", - Description: "Time stamp format pattern contains a valid format symbol that cannot be applied to time stamp parsing in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorTimestampFormatPatternDuplicateFields: { - Code: "EvaluatorTimestampFormatPatternDuplicateFields", - Description: "Time stamp format pattern contains multiple format specifiers representing the time stamp field in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch: { - Code: "EvaluatorUnterminatedTimestampFormatPatternToken", - Description: "Time stamp format pattern contains unterminated token in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorUnterminatedTimestampFormatPatternToken: { - Code: "EvaluatorInvalidTimestampFormatPatternToken", - Description: "Time stamp format pattern contains an invalid token in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorInvalidTimestampFormatPatternToken: { - Code: "EvaluatorInvalidTimestampFormatPatternToken", - Description: "Time stamp format pattern contains an invalid token in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorInvalidTimestampFormatPatternSymbol: { - Code: "EvaluatorInvalidTimestampFormatPatternSymbol", - Description: "Time stamp format pattern contains an invalid symbol in the SQL expression.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrEvaluatorBindingDoesNotExist: { - Code: "ErrEvaluatorBindingDoesNotExist", - Description: "A column name or a path provided does not exist in the SQL expression", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrMissingHeaders: { - Code: "MissingHeaders", - Description: "Some headers in the query are missing from the file. Check the file and try again.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidColumnIndex: { - Code: "InvalidColumnIndex", - Description: "The column index is invalid. Please check the service documentation and try again.", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrInvalidDecompressedSize: { - Code: "XMinioInvalidDecompressedSize", - Description: "The data provided is unfit for decompression", - HTTPStatusCode: http.StatusBadRequest, - }, - ErrAddUserInvalidArgument: { - Code: "XMinioInvalidIAMCredentials", - Description: "User is not allowed to be same as admin access key", - HTTPStatusCode: http.StatusForbidden, - }, - ErrAdminAccountNotEligible: { - Code: "XMinioInvalidIAMCredentials", - Description: "The administrator key is not eligible for this operation", - HTTPStatusCode: http.StatusForbidden, - }, - ErrAccountNotEligible: { - Code: "XMinioInvalidIAMCredentials", - Description: "The account key is not eligible for this operation", - HTTPStatusCode: http.StatusForbidden, - }, - ErrAdminServiceAccountNotFound: { - Code: "XMinioInvalidIAMCredentials", - Description: "The specified service account is not found", - HTTPStatusCode: http.StatusNotFound, - }, - ErrPostPolicyConditionInvalidFormat: { - Code: "PostPolicyInvalidKeyName", - Description: "Invalid according to Policy: Policy Condition failed", - HTTPStatusCode: http.StatusForbidden, - }, - // Add your error structure here. -} - -// toAPIErrorCode - Converts embedded errors. Convenience -// function written to handle all cases where we have known types of -// errors returned by underlying layers. -func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) { - if err == nil { - return ErrNone - } - - // Only return ErrClientDisconnected if the provided context is actually canceled. - // This way downstream context.Canceled will still report ErrOperationTimedOut - select { - case <-ctx.Done(): - if ctx.Err() == context.Canceled { - return ErrClientDisconnected - } - default: - } - - switch err { - case errInvalidArgument: - apiErr = ErrAdminInvalidArgument - case errNoSuchUser: - apiErr = ErrAdminNoSuchUser - case errNoSuchServiceAccount: - apiErr = ErrAdminServiceAccountNotFound - case errNoSuchGroup: - apiErr = ErrAdminNoSuchGroup - case errGroupNotEmpty: - apiErr = ErrAdminGroupNotEmpty - case errNoSuchPolicy: - apiErr = ErrAdminNoSuchPolicy - case errSignatureMismatch: - apiErr = ErrSignatureDoesNotMatch - case errInvalidRange: - apiErr = ErrInvalidRange - case errDataTooLarge: - apiErr = ErrEntityTooLarge - case errDataTooSmall: - apiErr = ErrEntityTooSmall - case errAuthentication: - apiErr = ErrAccessDenied - case auth.ErrInvalidAccessKeyLength: - apiErr = ErrAdminInvalidAccessKey - case auth.ErrInvalidSecretKeyLength: - apiErr = ErrAdminInvalidSecretKey - case errInvalidStorageClass: - apiErr = ErrInvalidStorageClass - // SSE errors - case errInvalidEncryptionParameters: - apiErr = ErrInvalidEncryptionParameters - case crypto.ErrInvalidEncryptionMethod: - apiErr = ErrInvalidEncryptionMethod - case crypto.ErrInvalidCustomerAlgorithm: - apiErr = ErrInvalidSSECustomerAlgorithm - case crypto.ErrMissingCustomerKey: - apiErr = ErrMissingSSECustomerKey - case crypto.ErrMissingCustomerKeyMD5: - apiErr = ErrMissingSSECustomerKeyMD5 - case crypto.ErrCustomerKeyMD5Mismatch: - apiErr = ErrSSECustomerKeyMD5Mismatch - case errObjectTampered: - apiErr = ErrObjectTampered - case errEncryptedObject: - apiErr = ErrSSEEncryptedObject - case errInvalidSSEParameters: - apiErr = ErrInvalidSSECustomerParameters - case crypto.ErrInvalidCustomerKey, crypto.ErrSecretKeyMismatch: - apiErr = ErrAccessDenied // no access without correct key - case crypto.ErrIncompatibleEncryptionMethod: - apiErr = ErrIncompatibleEncryptionMethod - case errKMSNotConfigured: - apiErr = ErrKMSNotConfigured - case context.Canceled, context.DeadlineExceeded: - apiErr = ErrOperationTimedOut - case errDiskNotFound: - apiErr = ErrSlowDown - case objectlock.ErrInvalidRetentionDate: - apiErr = ErrInvalidRetentionDate - case objectlock.ErrPastObjectLockRetainDate: - apiErr = ErrPastObjectLockRetainDate - case objectlock.ErrUnknownWORMModeDirective: - apiErr = ErrUnknownWORMModeDirective - case objectlock.ErrObjectLockInvalidHeaders: - apiErr = ErrObjectLockInvalidHeaders - case objectlock.ErrMalformedXML: - apiErr = ErrMalformedXML - } - - // Compression errors - switch err { - case errInvalidDecompressedSize: - apiErr = ErrInvalidDecompressedSize - } - - if apiErr != ErrNone { - // If there was a match in the above switch case. - return apiErr - } - - // etcd specific errors, a key is always a bucket for us return - // ErrNoSuchBucket in such a case. - if err == dns.ErrNoEntriesFound { - return ErrNoSuchBucket - } - - switch err.(type) { - case StorageFull: - apiErr = ErrStorageFull - case hash.BadDigest: - apiErr = ErrBadDigest - case AllAccessDisabled: - apiErr = ErrAllAccessDisabled - case IncompleteBody: - apiErr = ErrIncompleteBody - case ObjectExistsAsDirectory: - apiErr = ErrObjectExistsAsDirectory - case PrefixAccessDenied: - apiErr = ErrAccessDenied - case BucketNameInvalid: - apiErr = ErrInvalidBucketName - case BucketNotFound: - apiErr = ErrNoSuchBucket - case BucketAlreadyOwnedByYou: - apiErr = ErrBucketAlreadyOwnedByYou - case BucketNotEmpty: - apiErr = ErrBucketNotEmpty - case BucketAlreadyExists: - apiErr = ErrBucketAlreadyExists - case BucketExists: - apiErr = ErrBucketAlreadyOwnedByYou - case ObjectNotFound: - apiErr = ErrNoSuchKey - case MethodNotAllowed: - apiErr = ErrMethodNotAllowed - case InvalidVersionID: - apiErr = ErrInvalidVersionID - case VersionNotFound: - apiErr = ErrNoSuchVersion - case ObjectAlreadyExists: - apiErr = ErrMethodNotAllowed - case ObjectNameInvalid: - apiErr = ErrInvalidObjectName - case ObjectNamePrefixAsSlash: - apiErr = ErrInvalidObjectNamePrefixSlash - case InvalidUploadID: - apiErr = ErrNoSuchUpload - case InvalidPart: - apiErr = ErrInvalidPart - case InsufficientWriteQuorum: - apiErr = ErrSlowDown - case InsufficientReadQuorum: - apiErr = ErrSlowDown - case InvalidMarkerPrefixCombination: - apiErr = ErrNotImplemented - case InvalidUploadIDKeyCombination: - apiErr = ErrNotImplemented - case MalformedUploadID: - apiErr = ErrNoSuchUpload - case PartTooSmall: - apiErr = ErrEntityTooSmall - case SignatureDoesNotMatch: - apiErr = ErrSignatureDoesNotMatch - case hash.SHA256Mismatch: - apiErr = ErrContentSHA256Mismatch - case ObjectTooLarge: - apiErr = ErrEntityTooLarge - case ObjectTooSmall: - apiErr = ErrEntityTooSmall - case NotImplemented: - apiErr = ErrNotImplemented - case PartTooBig: - apiErr = ErrEntityTooLarge - case UnsupportedMetadata: - apiErr = ErrUnsupportedMetadata - case BucketPolicyNotFound: - apiErr = ErrNoSuchBucketPolicy - case BucketLifecycleNotFound: - apiErr = ErrNoSuchLifecycleConfiguration - case BucketSSEConfigNotFound: - apiErr = ErrNoSuchBucketSSEConfig - case BucketTaggingNotFound: - apiErr = ErrBucketTaggingNotFound - case BucketObjectLockConfigNotFound: - apiErr = ErrObjectLockConfigurationNotFound - case BucketQuotaConfigNotFound: - apiErr = ErrAdminNoSuchQuotaConfiguration - case BucketReplicationConfigNotFound: - apiErr = ErrReplicationConfigurationNotFoundError - case BucketRemoteDestinationNotFound: - apiErr = ErrRemoteDestinationNotFoundError - case BucketReplicationDestinationMissingLock: - apiErr = ErrReplicationDestinationMissingLock - case BucketRemoteTargetNotFound: - apiErr = ErrRemoteTargetNotFoundError - case BucketRemoteConnectionErr: - apiErr = ErrReplicationRemoteConnectionError - case BucketRemoteAlreadyExists: - apiErr = ErrBucketRemoteAlreadyExists - case BucketRemoteLabelInUse: - apiErr = ErrBucketRemoteLabelInUse - case BucketRemoteArnTypeInvalid: - apiErr = ErrBucketRemoteArnTypeInvalid - case BucketRemoteArnInvalid: - apiErr = ErrBucketRemoteArnInvalid - case BucketRemoteRemoveDisallowed: - apiErr = ErrBucketRemoteRemoveDisallowed - case BucketRemoteTargetNotVersioned: - apiErr = ErrRemoteTargetNotVersionedError - case BucketReplicationSourceNotVersioned: - apiErr = ErrReplicationSourceNotVersionedError - case TransitionStorageClassNotFound: - apiErr = ErrTransitionStorageClassNotFoundError - case InvalidObjectState: - apiErr = ErrInvalidObjectState - - case BucketQuotaExceeded: - apiErr = ErrAdminBucketQuotaExceeded - case *event.ErrInvalidEventName: - apiErr = ErrEventNotification - case *event.ErrInvalidARN: - apiErr = ErrARNNotification - case *event.ErrARNNotFound: - apiErr = ErrARNNotification - case *event.ErrUnknownRegion: - apiErr = ErrRegionNotification - case *event.ErrInvalidFilterName: - apiErr = ErrFilterNameInvalid - case *event.ErrFilterNamePrefix: - apiErr = ErrFilterNamePrefix - case *event.ErrFilterNameSuffix: - apiErr = ErrFilterNameSuffix - case *event.ErrInvalidFilterValue: - apiErr = ErrFilterValueInvalid - case *event.ErrDuplicateEventName: - apiErr = ErrOverlappingConfigs - case *event.ErrDuplicateQueueConfiguration: - apiErr = ErrOverlappingFilterNotification - case *event.ErrUnsupportedConfiguration: - apiErr = ErrUnsupportedNotification - case OperationTimedOut: - apiErr = ErrOperationTimedOut - case BackendDown: - apiErr = ErrBackendDown - case ObjectNameTooLong: - apiErr = ErrKeyTooLongError - case dns.ErrInvalidBucketName: - apiErr = ErrInvalidBucketName - case dns.ErrBucketConflict: - apiErr = ErrBucketAlreadyExists - default: - var ie, iw int - // This work-around is to handle the issue golang/go#30648 - if _, ferr := fmt.Fscanf(strings.NewReader(err.Error()), - "request declared a Content-Length of %d but only wrote %d bytes", - &ie, &iw); ferr != nil { - apiErr = ErrInternalError - // Make sure to log the errors which we cannot translate - // to a meaningful S3 API errors. This is added to aid in - // debugging unexpected/unhandled errors. - logger.LogIf(ctx, err) - } else if ie > iw { - apiErr = ErrIncompleteBody - } else { - apiErr = ErrInternalError - // Make sure to log the errors which we cannot translate - // to a meaningful S3 API errors. This is added to aid in - // debugging unexpected/unhandled errors. - logger.LogIf(ctx, err) - } - } - - return apiErr -} - -var noError = APIError{} - -// toAPIError - Converts embedded errors. Convenience -// function written to handle all cases where we have known types of -// errors returned by underlying layers. -func toAPIError(ctx context.Context, err error) APIError { - if err == nil { - return noError - } - - var apiErr = errorCodes.ToAPIErr(toAPIErrorCode(ctx, err)) - e, ok := err.(dns.ErrInvalidBucketName) - if ok { - code := toAPIErrorCode(ctx, e) - apiErr = errorCodes.ToAPIErrWithErr(code, e) - } - - if apiErr.Code == "NotImplemented" { - switch e := err.(type) { - case NotImplemented: - desc := e.Error() - if desc == "" { - desc = apiErr.Description - } - apiErr = APIError{ - Code: apiErr.Code, - Description: desc, - HTTPStatusCode: apiErr.HTTPStatusCode, - } - return apiErr - } - } - - if apiErr.Code == "InternalError" { - // If we see an internal error try to interpret - // any underlying errors if possible depending on - // their internal error types. This code is only - // useful with gateway implementations. - switch e := err.(type) { - case InvalidArgument: - apiErr = APIError{ - Code: "InvalidArgument", - Description: e.Error(), - HTTPStatusCode: errorCodes[ErrInvalidRequest].HTTPStatusCode, - } - case *xml.SyntaxError: - apiErr = APIError{ - Code: "MalformedXML", - Description: fmt.Sprintf("%s (%s)", errorCodes[ErrMalformedXML].Description, - e.Error()), - HTTPStatusCode: errorCodes[ErrMalformedXML].HTTPStatusCode, - } - case url.EscapeError: - apiErr = APIError{ - Code: "XMinioInvalidObjectName", - Description: fmt.Sprintf("%s (%s)", errorCodes[ErrInvalidObjectName].Description, - e.Error()), - HTTPStatusCode: http.StatusBadRequest, - } - case versioning.Error: - apiErr = APIError{ - Code: "IllegalVersioningConfigurationException", - Description: fmt.Sprintf("Versioning configuration specified in the request is invalid. (%s)", e.Error()), - HTTPStatusCode: http.StatusBadRequest, - } - case lifecycle.Error: - apiErr = APIError{ - Code: "InvalidRequest", - Description: e.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - case replication.Error: - apiErr = APIError{ - Code: "MalformedXML", - Description: e.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - case tags.Error: - apiErr = APIError{ - Code: e.Code(), - Description: e.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - case policy.Error: - apiErr = APIError{ - Code: "MalformedPolicy", - Description: e.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - case crypto.Error: - apiErr = APIError{ - Code: "XMinIOEncryptionError", - Description: e.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - case minio.ErrorResponse: - apiErr = APIError{ - Code: e.Code, - Description: e.Message, - HTTPStatusCode: e.StatusCode, - } - if globalIsGateway && strings.Contains(e.Message, "KMS is not configured") { - apiErr = APIError{ - Code: "NotImplemented", - Description: e.Message, - HTTPStatusCode: http.StatusNotImplemented, - } - } - case *googleapi.Error: - apiErr = APIError{ - Code: "XGCSInternalError", - Description: e.Message, - HTTPStatusCode: e.Code, - } - // GCS may send multiple errors, just pick the first one - // since S3 only sends one Error XML response. - if len(e.Errors) >= 1 { - apiErr.Code = e.Errors[0].Reason - - } - case azblob.StorageError: - apiErr = APIError{ - Code: string(e.ServiceCode()), - Description: e.Error(), - HTTPStatusCode: e.Response().StatusCode, - } - // Add more Gateway SDKs here if any in future. - default: - if errors.Is(err, errMalformedEncoding) { - apiErr = APIError{ - Code: "BadRequest", - Description: err.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - } else if errors.Is(err, errChunkTooBig) { - apiErr = APIError{ - Code: "BadRequest", - Description: err.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - } else if errors.Is(err, strconv.ErrRange) { - apiErr = APIError{ - Code: "BadRequest", - Description: err.Error(), - HTTPStatusCode: http.StatusBadRequest, - } - } else { - apiErr = APIError{ - Code: apiErr.Code, - Description: fmt.Sprintf("%s: cause(%v)", apiErr.Description, err), - HTTPStatusCode: apiErr.HTTPStatusCode, - } - } - } - } - - return apiErr -} - -// getAPIError provides API Error for input API error code. -func getAPIError(code APIErrorCode) APIError { - if apiErr, ok := errorCodes[code]; ok { - return apiErr - } - return errorCodes.ToAPIErr(ErrInternalError) -} - -// getErrorResponse gets in standard error and resource value and -// provides a encodable populated response values -func getAPIErrorResponse(ctx context.Context, err APIError, resource, requestID, hostID string) APIErrorResponse { - reqInfo := logger.GetReqInfo(ctx) - return APIErrorResponse{ - Code: err.Code, - Message: err.Description, - BucketName: reqInfo.BucketName, - Key: reqInfo.ObjectName, - Resource: resource, - Region: globalServerRegion, - RequestID: requestID, - HostID: hostID, - } -} diff --git a/api/minio/api-response.go b/api/minio/api-response.go deleted file mode 100644 index dd329a3..0000000 --- a/api/minio/api-response.go +++ /dev/null @@ -1,842 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package cmd - -import ( - "context" - "encoding/base64" - "encoding/xml" - "fmt" - "net/http" - "net/url" - "path" - "strconv" - "strings" - "time" - - "github.com/minio/minio/internal/crypto" - "github.com/minio/minio/internal/handlers" - xhttp "github.com/minio/minio/internal/http" - "github.com/minio/minio/internal/logger" -) - -const ( - // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z - iso8601TimeFormat = "2006-01-02T15:04:05.000Z" // Reply date format with nanosecond precision. - maxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse. - maxDeleteList = 10000 // Limit number of objects deleted in a delete call. - maxUploadsList = 10000 // Limit number of uploads in a listUploadsResponse. - maxPartsList = 10000 // Limit number of parts in a listPartsResponse. -) - -// LocationResponse - format for location response. -type LocationResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"` - Location string `xml:",chardata"` -} - -// PolicyStatus captures information returned by GetBucketPolicyStatusHandler -type PolicyStatus struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ PolicyStatus" json:"-"` - IsPublic string -} - -// ListVersionsResponse - format for list bucket versions response. -type ListVersionsResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListVersionsResult" json:"-"` - - Name string - Prefix string - KeyMarker 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. - NextKeyMarker string `xml:"NextKeyMarker,omitempty"` - - // When the number of responses exceeds the value of MaxKeys, - // NextVersionIdMarker specifies the first object version not - // returned that satisfies the search criteria. Use this value - // for the version-id-marker request parameter in a subsequent request. - NextVersionIDMarker string `xml:"NextVersionIdMarker"` - - // Marks the last version of the Key returned in a truncated response. - VersionIDMarker string `xml:"VersionIdMarker"` - - MaxKeys int - Delimiter string - // A flag that indicates whether or not ListObjects returned all of the results - // that satisfied the search criteria. - IsTruncated bool - - CommonPrefixes []CommonPrefix - Versions []ObjectVersion - - // Encoding type used to encode object keys in the response. - EncodingType string `xml:"EncodingType,omitempty"` -} - -// 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"` -} - -// ListObjectsV2Response - format for list objects response. -type ListObjectsV2Response struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` - - Name string - Prefix string - StartAfter string `xml:"StartAfter,omitempty"` - // 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. - ContinuationToken string `xml:"ContinuationToken,omitempty"` - NextContinuationToken string `xml:"NextContinuationToken,omitempty"` - - KeyCount int - 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"` -} - -// Part container for part metadata. -type Part struct { - PartNumber int - LastModified string - ETag string - Size int64 -} - -// ListPartsResponse - format for list parts response. -type ListPartsResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"` - - Bucket string - Key string - UploadID string `xml:"UploadId"` - - Initiator Initiator - Owner Owner - - // The class of storage used to store the object. - StorageClass string - - PartNumberMarker int - NextPartNumberMarker int - MaxParts int - IsTruncated bool - - // List of parts. - Parts []Part `xml:"Part"` -} - -// ListMultipartUploadsResponse - format for list multipart uploads response. -type ListMultipartUploadsResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListMultipartUploadsResult" json:"-"` - - Bucket string - KeyMarker string - UploadIDMarker string `xml:"UploadIdMarker"` - NextKeyMarker string - NextUploadIDMarker string `xml:"NextUploadIdMarker"` - Delimiter string - Prefix string - EncodingType string `xml:"EncodingType,omitempty"` - MaxUploads int - IsTruncated bool - - // List of pending uploads. - Uploads []Upload `xml:"Upload"` - - // Delimed common prefixes. - CommonPrefixes []CommonPrefix -} - -// 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 -} - -// Upload container for in progress multipart upload -type Upload struct { - Key string - UploadID string `xml:"UploadId"` - Initiator Initiator - Owner Owner - StorageClass string - Initiated string -} - -// CommonPrefix container for prefix response in ListObjectsResponse -type CommonPrefix struct { - Prefix string -} - -// Bucket container for bucket metadata -type Bucket struct { - Name string - CreationDate string // time string of format "2006-01-02T15:04:05.000Z" -} - -// ObjectVersion container for object version metadata -type ObjectVersion struct { - Object - IsLatest bool - VersionID string `xml:"VersionId"` - - isDeleteMarker bool -} - -// MarshalXML - marshal ObjectVersion -func (o ObjectVersion) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if o.isDeleteMarker { - start.Name.Local = "DeleteMarker" - } else { - start.Name.Local = "Version" - } - - type objectVersionWrapper ObjectVersion - return e.EncodeElement(objectVersionWrapper(o), start) -} - -// StringMap is a map[string]string. -type StringMap map[string]string - -// MarshalXML - StringMap marshals into XML. -func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - - tokens := []xml.Token{start} - - for key, value := range s { - t := xml.StartElement{} - t.Name = xml.Name{ - Space: "", - Local: key, - } - tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) - } - - tokens = append(tokens, xml.EndElement{ - Name: start.Name, - }) - - for _, t := range tokens { - if err := e.EncodeToken(t); err != nil { - return err - } - } - - // flush to ensure tokens are written - return e.Flush() -} - -// 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"` -} - -// CopyObjectResponse container returns ETag and LastModified of the successfully copied object -type CopyObjectResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyObjectResult" json:"-"` - LastModified string // time string of format "2006-01-02T15:04:05.000Z" - ETag string // md5sum of the copied object. -} - -// CopyObjectPartResponse container returns ETag and LastModified of the successfully copied object -type CopyObjectPartResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyPartResult" json:"-"` - LastModified string // time string of format "2006-01-02T15:04:05.000Z" - ETag string // md5sum of the copied object part. -} - -// Initiator inherit from Owner struct, fields are same -type Initiator Owner - -// Owner - bucket owner/principal -type Owner struct { - ID string - DisplayName string -} - -// InitiateMultipartUploadResponse container for InitiateMultiPartUpload response, provides uploadID to start MultiPart upload -type InitiateMultipartUploadResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ InitiateMultipartUploadResult" json:"-"` - - Bucket string - Key string - UploadID string `xml:"UploadId"` -} - -// CompleteMultipartUploadResponse container for completed multipart upload response -type CompleteMultipartUploadResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUploadResult" json:"-"` - - Location string - Bucket string - Key string - ETag string -} - -// DeleteError structure. -type DeleteError struct { - Code string - Message string - Key string - VersionID string `xml:"VersionId"` -} - -// DeleteObjectsResponse container for multiple object deletes. -type DeleteObjectsResponse struct { - XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"` - - // Collection of all deleted objects - DeletedObjects []DeletedObject `xml:"Deleted,omitempty"` - - // Collection of errors deleting certain objects. - Errors []DeleteError `xml:"Error,omitempty"` -} - -// PostResponse container for POST object request when success_action_status is set to 201 -type PostResponse struct { - Bucket string - Key string - ETag string - Location string -} - -// returns "https" if the tls boolean is true, "http" otherwise. -func getURLScheme(tls bool) string { - if tls { - return httpsScheme - } - return httpScheme -} - -// getObjectLocation gets the fully qualified URL of an object. -func getObjectLocation(r *http.Request, domains []string, bucket, object string) string { - // unit tests do not have host set. - if r.Host == "" { - return path.Clean(r.URL.Path) - } - proto := handlers.GetSourceScheme(r) - if proto == "" { - proto = getURLScheme(globalIsTLS) - } - u := &url.URL{ - Host: r.Host, - Path: path.Join(SlashSeparator, bucket, object), - Scheme: proto, - } - // If domain is set then we need to use bucket DNS style. - for _, domain := range domains { - if strings.HasPrefix(r.Host, bucket+"."+domain) { - u.Path = path.Join(SlashSeparator, object) - break - } - } - return u.String() -} - -// generates ListBucketsResponse from array of BucketInfo which can be -// serialized to match XML and JSON API spec output. -func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse { - listbuckets := make([]Bucket, 0, len(buckets)) - var data = ListBucketsResponse{} - var owner = Owner{ - ID: globalMinioDefaultOwnerID, - DisplayName: "minio", - } - - for _, bucket := range buckets { - var listbucket = Bucket{} - listbucket.Name = bucket.Name - listbucket.CreationDate = bucket.Created.UTC().Format(iso8601TimeFormat) - listbuckets = append(listbuckets, listbucket) - } - - data.Owner = owner - data.Buckets.Buckets = listbuckets - - return data -} - -// generates an ListBucketVersions response for the said bucket with other enumerated options. -func generateListVersionsResponse(bucket, prefix, marker, versionIDMarker, delimiter, encodingType string, maxKeys int, resp ListObjectVersionsInfo) ListVersionsResponse { - versions := make([]ObjectVersion, 0, len(resp.Objects)) - var owner = Owner{ - ID: globalMinioDefaultOwnerID, - DisplayName: "minio", - } - var data = ListVersionsResponse{} - - for _, object := range resp.Objects { - var content = ObjectVersion{} - if object.Name == "" { - continue - } - content.Key = s3EncodeName(object.Name, encodingType) - content.LastModified = object.ModTime.UTC().Format(iso8601TimeFormat) - if object.ETag != "" { - content.ETag = "\"" + object.ETag + "\"" - } - content.Size = object.Size - if object.StorageClass != "" { - content.StorageClass = object.StorageClass - } else { - content.StorageClass = globalMinioDefaultStorageClass - } - content.Owner = owner - content.VersionID = object.VersionID - if content.VersionID == "" { - content.VersionID = nullVersionID - } - content.IsLatest = object.IsLatest - content.isDeleteMarker = object.DeleteMarker - versions = append(versions, content) - } - - data.Name = bucket - data.Versions = versions - data.EncodingType = encodingType - data.Prefix = s3EncodeName(prefix, encodingType) - data.KeyMarker = s3EncodeName(marker, encodingType) - data.Delimiter = s3EncodeName(delimiter, encodingType) - data.MaxKeys = maxKeys - - data.NextKeyMarker = s3EncodeName(resp.NextMarker, encodingType) - data.NextVersionIDMarker = resp.NextVersionIDMarker - data.VersionIDMarker = versionIDMarker - data.IsTruncated = resp.IsTruncated - - prefixes := make([]CommonPrefix, 0, len(resp.Prefixes)) - for _, prefix := range resp.Prefixes { - var prefixItem = CommonPrefix{} - prefixItem.Prefix = s3EncodeName(prefix, encodingType) - prefixes = append(prefixes, prefixItem) - } - data.CommonPrefixes = prefixes - return data -} - -// generates an ListObjectsV1 response for the said bucket with other enumerated options. -func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse { - contents := make([]Object, 0, len(resp.Objects)) - var owner = Owner{ - ID: globalMinioDefaultOwnerID, - DisplayName: "minio", - } - var data = ListObjectsResponse{} - - for _, object := range resp.Objects { - var content = Object{} - if object.Name == "" { - continue - } - content.Key = s3EncodeName(object.Name, encodingType) - content.LastModified = object.ModTime.UTC().Format(iso8601TimeFormat) - if object.ETag != "" { - content.ETag = "\"" + object.ETag + "\"" - } - content.Size = object.Size - if object.StorageClass != "" { - content.StorageClass = object.StorageClass - } else { - content.StorageClass = globalMinioDefaultStorageClass - } - content.Owner = owner - contents = append(contents, content) - } - data.Name = bucket - data.Contents = contents - - data.EncodingType = encodingType - data.Prefix = s3EncodeName(prefix, encodingType) - data.Marker = s3EncodeName(marker, encodingType) - data.Delimiter = s3EncodeName(delimiter, encodingType) - data.MaxKeys = maxKeys - data.NextMarker = s3EncodeName(resp.NextMarker, encodingType) - data.IsTruncated = resp.IsTruncated - - prefixes := make([]CommonPrefix, 0, len(resp.Prefixes)) - for _, prefix := range resp.Prefixes { - var prefixItem = CommonPrefix{} - prefixItem.Prefix = s3EncodeName(prefix, encodingType) - prefixes = append(prefixes, prefixItem) - } - data.CommonPrefixes = prefixes - return data -} - -// generates an ListObjectsV2 response for the said bucket with other enumerated options. -func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter, encodingType string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string, metadata bool) ListObjectsV2Response { - contents := make([]Object, 0, len(objects)) - var owner = Owner{ - ID: globalMinioDefaultOwnerID, - DisplayName: "minio", - } - var data = ListObjectsV2Response{} - - for _, object := range objects { - var content = Object{} - if object.Name == "" { - continue - } - content.Key = s3EncodeName(object.Name, encodingType) - content.LastModified = object.ModTime.UTC().Format(iso8601TimeFormat) - if object.ETag != "" { - content.ETag = "\"" + object.ETag + "\"" - } - content.Size = object.Size - if object.StorageClass != "" { - content.StorageClass = object.StorageClass - } else { - content.StorageClass = globalMinioDefaultStorageClass - } - content.Owner = owner - if metadata { - content.UserMetadata = make(StringMap) - switch kind, _ := crypto.IsEncrypted(object.UserDefined); kind { - case crypto.S3: - content.UserMetadata[xhttp.AmzServerSideEncryption] = xhttp.AmzEncryptionAES - case crypto.S3KMS: - content.UserMetadata[xhttp.AmzServerSideEncryption] = xhttp.AmzEncryptionKMS - case crypto.SSEC: - content.UserMetadata[xhttp.AmzServerSideEncryptionCustomerAlgorithm] = xhttp.AmzEncryptionAES - } - for k, v := range CleanMinioInternalMetadataKeys(object.UserDefined) { - if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { - // Do not need to send any internal metadata - // values to client. - continue - } - // https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w - if equals(k, xhttp.AmzMetaUnencryptedContentLength, xhttp.AmzMetaUnencryptedContentMD5) { - continue - } - content.UserMetadata[k] = v - } - } - contents = append(contents, content) - } - data.Name = bucket - data.Contents = contents - - data.EncodingType = encodingType - data.StartAfter = s3EncodeName(startAfter, encodingType) - data.Delimiter = s3EncodeName(delimiter, encodingType) - data.Prefix = s3EncodeName(prefix, encodingType) - data.MaxKeys = maxKeys - data.ContinuationToken = base64.StdEncoding.EncodeToString([]byte(token)) - data.NextContinuationToken = base64.StdEncoding.EncodeToString([]byte(nextToken)) - data.IsTruncated = isTruncated - - commonPrefixes := make([]CommonPrefix, 0, len(prefixes)) - for _, prefix := range prefixes { - var prefixItem = CommonPrefix{} - prefixItem.Prefix = s3EncodeName(prefix, encodingType) - commonPrefixes = append(commonPrefixes, prefixItem) - } - data.CommonPrefixes = commonPrefixes - data.KeyCount = len(data.Contents) + len(data.CommonPrefixes) - return data -} - -// generates CopyObjectResponse from etag and lastModified time. -func generateCopyObjectResponse(etag string, lastModified time.Time) CopyObjectResponse { - return CopyObjectResponse{ - ETag: "\"" + etag + "\"", - LastModified: lastModified.UTC().Format(iso8601TimeFormat), - } -} - -// generates CopyObjectPartResponse from etag and lastModified time. -func generateCopyObjectPartResponse(etag string, lastModified time.Time) CopyObjectPartResponse { - return CopyObjectPartResponse{ - ETag: "\"" + etag + "\"", - LastModified: lastModified.UTC().Format(iso8601TimeFormat), - } -} - -// generates InitiateMultipartUploadResponse for given bucket, key and uploadID. -func generateInitiateMultipartUploadResponse(bucket, key, uploadID string) InitiateMultipartUploadResponse { - return InitiateMultipartUploadResponse{ - Bucket: bucket, - Key: key, - UploadID: uploadID, - } -} - -// generates CompleteMultipartUploadResponse for given bucket, key, location and ETag. -func generateCompleteMultpartUploadResponse(bucket, key, location, etag string) CompleteMultipartUploadResponse { - return CompleteMultipartUploadResponse{ - Location: location, - Bucket: bucket, - Key: key, - // AWS S3 quotes the ETag in XML, make sure we are compatible here. - ETag: "\"" + etag + "\"", - } -} - -// generates ListPartsResponse from ListPartsInfo. -func generateListPartsResponse(partsInfo ListPartsInfo, encodingType string) ListPartsResponse { - listPartsResponse := ListPartsResponse{} - listPartsResponse.Bucket = partsInfo.Bucket - listPartsResponse.Key = s3EncodeName(partsInfo.Object, encodingType) - listPartsResponse.UploadID = partsInfo.UploadID - listPartsResponse.StorageClass = globalMinioDefaultStorageClass - - // Dumb values not meaningful - listPartsResponse.Initiator = Initiator{ - ID: globalMinioDefaultOwnerID, - DisplayName: globalMinioDefaultOwnerID, - } - listPartsResponse.Owner = Owner{ - ID: globalMinioDefaultOwnerID, - DisplayName: globalMinioDefaultOwnerID, - } - - listPartsResponse.MaxParts = partsInfo.MaxParts - listPartsResponse.PartNumberMarker = partsInfo.PartNumberMarker - listPartsResponse.IsTruncated = partsInfo.IsTruncated - listPartsResponse.NextPartNumberMarker = partsInfo.NextPartNumberMarker - - listPartsResponse.Parts = make([]Part, len(partsInfo.Parts)) - for index, part := range partsInfo.Parts { - newPart := Part{} - newPart.PartNumber = part.PartNumber - newPart.ETag = "\"" + part.ETag + "\"" - newPart.Size = part.Size - newPart.LastModified = part.LastModified.UTC().Format(iso8601TimeFormat) - listPartsResponse.Parts[index] = newPart - } - return listPartsResponse -} - -// generates ListMultipartUploadsResponse for given bucket and ListMultipartsInfo. -func generateListMultipartUploadsResponse(bucket string, multipartsInfo ListMultipartsInfo, encodingType string) ListMultipartUploadsResponse { - listMultipartUploadsResponse := ListMultipartUploadsResponse{} - listMultipartUploadsResponse.Bucket = bucket - listMultipartUploadsResponse.Delimiter = s3EncodeName(multipartsInfo.Delimiter, encodingType) - listMultipartUploadsResponse.IsTruncated = multipartsInfo.IsTruncated - listMultipartUploadsResponse.EncodingType = encodingType - listMultipartUploadsResponse.Prefix = s3EncodeName(multipartsInfo.Prefix, encodingType) - listMultipartUploadsResponse.KeyMarker = s3EncodeName(multipartsInfo.KeyMarker, encodingType) - listMultipartUploadsResponse.NextKeyMarker = s3EncodeName(multipartsInfo.NextKeyMarker, encodingType) - listMultipartUploadsResponse.MaxUploads = multipartsInfo.MaxUploads - listMultipartUploadsResponse.NextUploadIDMarker = multipartsInfo.NextUploadIDMarker - listMultipartUploadsResponse.UploadIDMarker = multipartsInfo.UploadIDMarker - listMultipartUploadsResponse.CommonPrefixes = make([]CommonPrefix, len(multipartsInfo.CommonPrefixes)) - for index, commonPrefix := range multipartsInfo.CommonPrefixes { - listMultipartUploadsResponse.CommonPrefixes[index] = CommonPrefix{ - Prefix: s3EncodeName(commonPrefix, encodingType), - } - } - listMultipartUploadsResponse.Uploads = make([]Upload, len(multipartsInfo.Uploads)) - for index, upload := range multipartsInfo.Uploads { - newUpload := Upload{} - newUpload.UploadID = upload.UploadID - newUpload.Key = s3EncodeName(upload.Object, encodingType) - newUpload.Initiated = upload.Initiated.UTC().Format(iso8601TimeFormat) - listMultipartUploadsResponse.Uploads[index] = newUpload - } - return listMultipartUploadsResponse -} - -// generate multi objects delete response. -func generateMultiDeleteResponse(quiet bool, deletedObjects []DeletedObject, errs []DeleteError) DeleteObjectsResponse { - deleteResp := DeleteObjectsResponse{} - if !quiet { - deleteResp.DeletedObjects = deletedObjects - } - if len(errs) == len(deletedObjects) { - deleteResp.DeletedObjects = nil - } - deleteResp.Errors = errs - return deleteResp -} - -func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { - setCommonHeaders(w) - if mType != mimeNone { - w.Header().Set(xhttp.ContentType, string(mType)) - } - w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(response))) - w.WriteHeader(statusCode) - if response != nil { - w.Write(response) - w.(http.Flusher).Flush() - } -} - -// mimeType represents various MIME type used API responses. -type mimeType string - -const ( - // Means no response type. - mimeNone mimeType = "" - // Means response type is JSON. - mimeJSON mimeType = "application/json" - // Means response type is XML. - mimeXML mimeType = "application/xml" -) - -// writeSuccessResponseJSON writes success headers and response if any, -// with content-type set to `application/json`. -func writeSuccessResponseJSON(w http.ResponseWriter, response []byte) { - writeResponse(w, http.StatusOK, response, mimeJSON) -} - -// writeSuccessResponseXML writes success headers and response if any, -// with content-type set to `application/xml`. -func writeSuccessResponseXML(w http.ResponseWriter, response []byte) { - writeResponse(w, http.StatusOK, response, mimeXML) -} - -// writeSuccessNoContent writes success headers with http status 204 -func writeSuccessNoContent(w http.ResponseWriter) { - writeResponse(w, http.StatusNoContent, nil, mimeNone) -} - -// writeRedirectSeeOther writes Location header with http status 303 -func writeRedirectSeeOther(w http.ResponseWriter, location string) { - w.Header().Set(xhttp.Location, location) - writeResponse(w, http.StatusSeeOther, nil, mimeNone) -} - -func writeSuccessResponseHeadersOnly(w http.ResponseWriter) { - writeResponse(w, http.StatusOK, nil, mimeNone) -} - -// writeErrorRespone writes error headers -func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { - switch err.Code { - case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": - // Set retry-after header to indicate user-agents to retry request after 120secs. - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set(xhttp.RetryAfter, "120") - case "InvalidRegion": - err.Description = fmt.Sprintf("Region does not match; expecting '%s'.", globalServerRegion) - case "AuthorizationHeaderMalformed": - err.Description = fmt.Sprintf("The authorization header is malformed; the region is wrong; expecting '%s'.", globalServerRegion) - } - - // Generate error response. - errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, - w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) - encodedErrorResponse := encodeResponse(errorResponse) - writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) -} - -func writeErrorResponseHeadersOnly(w http.ResponseWriter, err APIError) { - writeResponse(w, err.HTTPStatusCode, nil, mimeNone) -} - -func writeErrorResponseString(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { - // Generate string error response. - writeResponse(w, err.HTTPStatusCode, []byte(err.Description), mimeNone) -} - -// writeErrorResponseJSON - writes error response in JSON format; -// useful for admin APIs. -func writeErrorResponseJSON(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { - // Generate error response. - errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) - encodedErrorResponse := encodeResponseJSON(errorResponse) - writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON) -} - -// writeCustomErrorResponseJSON - similar to writeErrorResponseJSON, -// but accepts the error message directly (this allows messages to be -// dynamically generated.) -func writeCustomErrorResponseJSON(ctx context.Context, w http.ResponseWriter, err APIError, - errBody string, reqURL *url.URL) { - - reqInfo := logger.GetReqInfo(ctx) - errorResponse := APIErrorResponse{ - Code: err.Code, - Message: errBody, - Resource: reqURL.Path, - BucketName: reqInfo.BucketName, - Key: reqInfo.ObjectName, - RequestID: w.Header().Get(xhttp.AmzRequestID), - HostID: globalDeploymentID, - } - encodedErrorResponse := encodeResponseJSON(errorResponse) - writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON) -} diff --git a/api/minio/api-utils.go b/api/minio/api-utils.go deleted file mode 100644 index fa277b9..0000000 --- a/api/minio/api-utils.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package cmd - -import ( - "strings" -) - -func shouldEscape(c byte) bool { - if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { - return false - } - - switch c { - case '-', '_', '.', '/', '*': - return false - } - return true -} - -// s3URLEncode is based on Golang's url.QueryEscape() code, -// while considering some S3 exceptions: -// - Avoid encoding '/' and '*' -// - Force encoding of '~' -func s3URLEncode(s string) string { - spaceCount, hexCount := 0, 0 - for i := 0; i < len(s); i++ { - c := s[i] - if shouldEscape(c) { - if c == ' ' { - spaceCount++ - } else { - hexCount++ - } - } - } - - if spaceCount == 0 && hexCount == 0 { - return s - } - - var buf [64]byte - var t []byte - - required := len(s) + 2*hexCount - if required <= len(buf) { - t = buf[:required] - } else { - t = make([]byte, required) - } - - if hexCount == 0 { - copy(t, s) - for i := 0; i < len(s); i++ { - if s[i] == ' ' { - t[i] = '+' - } - } - return string(t) - } - - j := 0 - for i := 0; i < len(s); i++ { - switch c := s[i]; { - case c == ' ': - t[j] = '+' - j++ - case shouldEscape(c): - t[j] = '%' - t[j+1] = "0123456789ABCDEF"[c>>4] - t[j+2] = "0123456789ABCDEF"[c&15] - j += 3 - default: - t[j] = s[i] - j++ - } - } - return string(t) -} - -// s3EncodeName encodes string in response when encodingType is specified in AWS S3 requests. -func s3EncodeName(name string, encodingType string) (result string) { - // Quick path to exit - if encodingType == "" { - return name - } - encodingType = strings.ToLower(encodingType) - switch encodingType { - case "url": - return s3URLEncode(name) - } - return name -} diff --git a/api/minio/header.go b/api/minio/header.go deleted file mode 100644 index 58903c4..0000000 --- a/api/minio/header.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package crypto - -import ( - "bytes" - "crypto/md5" - "encoding/base64" - "net/http" - - xhttp "github.com/minio/minio/internal/http" -) - -// RemoveSensitiveHeaders removes confidential encryption -// information - e.g. the SSE-C key - from the HTTP headers. -// It has the same semantics as RemoveSensitiveEntires. -func RemoveSensitiveHeaders(h http.Header) { - h.Del(xhttp.AmzServerSideEncryptionCustomerKey) - h.Del(xhttp.AmzServerSideEncryptionCopyCustomerKey) - h.Del(xhttp.AmzMetaUnencryptedContentLength) - h.Del(xhttp.AmzMetaUnencryptedContentMD5) -} - -var ( - // SSECopy represents AWS SSE-C for copy requests. It provides - // functionality to handle SSE-C copy requests. - SSECopy = ssecCopy{} -) - -type ssecCopy struct{} - -// IsRequested returns true if the HTTP headers contains -// at least one SSE-C copy header. Regular SSE-C headers -// are ignored. -func (ssecCopy) IsRequested(h http.Header) bool { - if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm]; ok { - return true - } - if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerKey]; ok { - return true - } - if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5]; ok { - return true - } - return false -} - -// ParseHTTP parses the SSE-C copy headers and returns the SSE-C client key -// on success. Regular SSE-C headers are ignored. -func (ssecCopy) ParseHTTP(h http.Header) (key [32]byte, err error) { - if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm) != xhttp.AmzEncryptionAES { - return key, ErrInvalidCustomerAlgorithm - } - if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey) == "" { - return key, ErrMissingCustomerKey - } - if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5) == "" { - return key, ErrMissingCustomerKeyMD5 - } - - clientKey, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)) - if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long - return key, ErrInvalidCustomerKey - } - keyMD5, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5)) - if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) { - return key, ErrCustomerKeyMD5Mismatch - } - copy(key[:], clientKey) - return key, nil -} diff --git a/api/minio/proxy.go b/api/minio/proxy.go deleted file mode 100644 index f23d4fd..0000000 --- a/api/minio/proxy.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -// Originally from https://github.com/gorilla/handlers with following license -// https://raw.githubusercontent.com/gorilla/handlers/master/LICENSE, forked -// and heavily modified for MinIO's internal needs. - -package handlers - -import ( - "net" - "net/http" - "regexp" - "strings" -) - -var ( - // De-facto standard header keys. - xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") - xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") - xForwardedPort = http.CanonicalHeaderKey("X-Forwarded-Port") - xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") - xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme") - 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 - forwarded = http.CanonicalHeaderKey("Forwarded") - // Allows for a sub-match of the first value after 'for=' to the next - // comma, semi-colon or space. The match is case-insensitive. - forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)(.*)`) - // Allows for a sub-match for the first instance of scheme (http|https) - // prefixed by 'proto='. The match is case-insensitive. - protoRegex = regexp.MustCompile(`(?i)^(;|,| )+(?:proto=)(https|http)`) -) - -// GetSourceScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 -// Forwarded headers (in that order). -func GetSourceScheme(r *http.Request) string { - var scheme string - - // Retrieve the scheme from X-Forwarded-Proto. - if proto := r.Header.Get(xForwardedProto); proto != "" { - scheme = strings.ToLower(proto) - } else if proto = r.Header.Get(xForwardedScheme); proto != "" { - scheme = strings.ToLower(proto) - } else if proto := r.Header.Get(forwarded); proto != "" { - // match should contain at least two elements if the protocol was - // specified in the Forwarded header. The first element will always be - // the 'for=', which we ignore, subsequently we proceed to look for - // 'proto=' which should precede right after `for=` if not - // we simply ignore the values and return empty. This is in line - // with the approach we took for returning first ip from multiple - // params. - if match := forRegex.FindStringSubmatch(proto); len(match) > 1 { - if match = protoRegex.FindStringSubmatch(match[2]); len(match) > 1 { - scheme = strings.ToLower(match[2]) - } - } - } - - return scheme -} - -// GetSourceIPFromHeaders retrieves the IP from the X-Forwarded-For, X-Real-IP -// and RFC7239 Forwarded headers (in that order) -func GetSourceIPFromHeaders(r *http.Request) string { - var addr string - - if fwd := r.Header.Get(xForwardedFor); fwd != "" { - // Only grab 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 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], `"`) - } - } - - return addr -} - -// GetSourceIP retrieves the IP from the request headers -// and falls back to r.RemoteAddr when necessary. -func GetSourceIP(r *http.Request) string { - addr := GetSourceIPFromHeaders(r) - if addr != "" { - return addr - } - - // Default to remote address if headers not set. - addr, _, _ = net.SplitHostPort(r.RemoteAddr) - return addr -} diff --git a/api/minio/reqinfo.go b/api/minio/reqinfo.go deleted file mode 100644 index 56e8c3e..0000000 --- a/api/minio/reqinfo.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package logger - -import ( - "context" - "fmt" - "sync" -) - -// Key used for Get/SetReqInfo -type contextKeyType string - -const contextLogKey = contextKeyType("miniolog") - -// KeyVal - appended to ReqInfo.Tags -type KeyVal struct { - Key string - Val interface{} -} - -// ReqInfo stores the request info. -type ReqInfo struct { - RemoteHost string // Client Host/IP - Host string // Node Host/IP - UserAgent string // User Agent - DeploymentID string // x-minio-deployment-id - RequestID string // x-amz-request-id - API string // API name - GetObject PutObject NewMultipartUpload etc. - BucketName string // Bucket name - ObjectName string // Object name - AccessKey string // Access Key - tags []KeyVal // Any additional info not accommodated by above fields - sync.RWMutex -} - -// NewReqInfo : -func NewReqInfo(remoteHost, userAgent, deploymentID, requestID, api, bucket, object string) *ReqInfo { - req := ReqInfo{} - req.RemoteHost = remoteHost - req.UserAgent = userAgent - req.API = api - req.DeploymentID = deploymentID - req.RequestID = requestID - req.BucketName = bucket - req.ObjectName = object - return &req -} - -// AppendTags - appends key/val to ReqInfo.tags -func (r *ReqInfo) AppendTags(key string, val interface{}) *ReqInfo { - if r == nil { - return nil - } - r.Lock() - defer r.Unlock() - r.tags = append(r.tags, KeyVal{key, val}) - return r -} - -// SetTags - sets key/val to ReqInfo.tags -func (r *ReqInfo) SetTags(key string, val interface{}) *ReqInfo { - if r == nil { - return nil - } - r.Lock() - defer r.Unlock() - // Search of tag key already exists in tags - var updated bool - for _, tag := range r.tags { - if tag.Key == key { - tag.Val = val - updated = true - break - } - } - if !updated { - // Append to the end of tags list - r.tags = append(r.tags, KeyVal{key, val}) - } - return r -} - -// GetTags - returns the user defined tags -func (r *ReqInfo) GetTags() []KeyVal { - if r == nil { - return nil - } - r.RLock() - defer r.RUnlock() - return append([]KeyVal(nil), r.tags...) -} - -// GetTagsMap - returns the user defined tags in a map structure -func (r *ReqInfo) GetTagsMap() map[string]interface{} { - if r == nil { - return nil - } - r.RLock() - defer r.RUnlock() - m := make(map[string]interface{}, len(r.tags)) - for _, t := range r.tags { - m[t.Key] = t.Val - } - return m -} - -// SetReqInfo sets ReqInfo in the context. -func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context { - if ctx == nil { - LogIf(context.Background(), fmt.Errorf("context is nil")) - return nil - } - return context.WithValue(ctx, contextLogKey, req) -} - -// GetReqInfo returns ReqInfo if set. -func GetReqInfo(ctx context.Context) *ReqInfo { - if ctx != nil { - r, ok := ctx.Value(contextLogKey).(*ReqInfo) - if ok { - return r - } - r = &ReqInfo{} - SetReqInfo(ctx, r) - return r - } - return nil -} diff --git a/api/reqinfo.go b/api/reqinfo.go index dfc6c54..100a12d 100644 --- a/api/reqinfo.go +++ b/api/reqinfo.go @@ -1,6 +1,39 @@ package api +import ( + "context" + "net" + "net/http" + "net/url" + "regexp" + "strings" + "sync" + + "github.com/gorilla/mux" +) + type ( + // KeyVal - appended to ReqInfo.Tags. + KeyVal struct { + Key string + Val string + } + + // ReqInfo stores the request info. + ReqInfo struct { + sync.RWMutex + RemoteHost string // Client Host/IP + Host string // Node Host/IP + UserAgent string // User Agent + DeploymentID string // x-minio-deployment-id + RequestID string // x-amz-request-id + API string // API name - GetObject PutObject NewMultipartUpload etc. + BucketName string // Bucket name + ObjectName string // Object name + URL *url.URL // Request url + tags []KeyVal // Any additional info not accommodated by above fields + } + // ObjectRequest represents object request data. ObjectRequest struct { Bucket string @@ -13,3 +46,159 @@ type ( type contextKeyType string const ctxRequestInfo = contextKeyType("NeoFS-S3-GW") + +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. + forwarded = http.CanonicalHeaderKey("Forwarded") + // Allows for a sub-match of the first value after 'for=' to the next + // comma, semi-colon or space. The match is case-insensitive. + 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 all +// else fails. +func GetSourceIP(r *http.Request) string { + var addr string + + if fwd := r.Header.Get(xForwardedFor); fwd != "" { + // Only grab 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 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 prepareContext(w http.ResponseWriter, r *http.Request) context.Context { + 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 SetReqInfo(r.Context(), + // prepare request info + 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{ + API: req.Method, + BucketName: req.Bucket, + ObjectName: req.Object, + UserAgent: r.UserAgent(), + RemoteHost: GetSourceIP(r), + RequestID: GetRequestID(w), + DeploymentID: deploymentID.String(), + URL: r.URL, + } +} + +// AppendTags - appends key/val to ReqInfo.tags. +func (r *ReqInfo) AppendTags(key string, val string) *ReqInfo { + if r == nil { + return nil + } + r.Lock() + defer r.Unlock() + r.tags = append(r.tags, KeyVal{key, val}) + return r +} + +// SetTags - sets key/val to ReqInfo.tags. +func (r *ReqInfo) SetTags(key string, val string) *ReqInfo { + if r == nil { + return nil + } + r.Lock() + defer r.Unlock() + // Search of tag key already exists in tags + var updated bool + for _, tag := range r.tags { + if tag.Key == key { + tag.Val = val + updated = true + break + } + } + if !updated { + // Append to the end of tags list + r.tags = append(r.tags, KeyVal{key, val}) + } + return r +} + +// GetTags - returns the user defined tags. +func (r *ReqInfo) GetTags() []KeyVal { + if r == nil { + return nil + } + r.RLock() + defer r.RUnlock() + return append([]KeyVal(nil), r.tags...) +} + +// SetReqInfo sets ReqInfo in the context. +func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context { + if ctx == nil { + return nil + } + return context.WithValue(ctx, ctxRequestInfo, req) +} + +// GetReqInfo returns ReqInfo if set. +func GetReqInfo(ctx context.Context) *ReqInfo { + if ctx == nil { + return &ReqInfo{} + } else if r, ok := ctx.Value(ctxRequestInfo).(*ReqInfo); ok { + return r + } + return &ReqInfo{} +} diff --git a/api/response.go b/api/response.go index 3e407d1..b70b1c8 100644 --- a/api/response.go +++ b/api/response.go @@ -1,11 +1,15 @@ package api import ( + "bytes" "encoding/xml" "fmt" + "net/http" + "strconv" "github.com/google/uuid" "github.com/nspcc-dev/neofs-s3-gw/api/errors" + "github.com/nspcc-dev/neofs-s3-gw/internal/version" ) type ( @@ -105,6 +109,109 @@ var s3ErrorResponseMap = map[string]string{ // Add new API errors here. } +// WriteErrorResponse writes error headers. +func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) { + code := http.StatusInternalServerError + + if e, ok := err.(errors.Error); ok { + code = e.HTTPStatusCode + + switch e.Code { + case "SlowDown", "XNeoFSServerNotInitialized", "XNeoFSReadQuorum", "XNeoFSWriteQuorum": + // Set retry-after header to indicate user-agents to retry request after 120secs. + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + w.Header().Set(hdrRetryAfter, "120") + case "AccessDenied": + // TODO process when the request is from browser and also if browser + } + } + + // Generate error response. + errorResponse := getAPIErrorResponse(reqInfo, err) + encodedErrorResponse := EncodeResponse(errorResponse) + WriteResponse(w, code, encodedErrorResponse, MimeXML) +} + +// 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: "XMinioUnknownAPIRequest", + Description: desc, + HTTPStatusCode: http.StatusBadRequest, + }) +} + +// Write http common headers. +func setCommonHeaders(w http.ResponseWriter) { + w.Header().Set(hdrServerInfo, version.Server) + w.Header().Set(hdrAcceptRanges, "bytes") + + // Remove sensitive information + removeSensitiveHeaders(w.Header()) +} + +// removeSensitiveHeaders removes confidential encryption +// information - e.g. the SSE-C key - from the HTTP headers. +// It has the same semantics as RemoveSensitiveEntries. +func removeSensitiveHeaders(h http.Header) { + h.Del(hdrSSECustomerKey) + h.Del(hdrSSECopyKey) +} + +// WriteResponse writes given statusCode and response into w (with mType header if set). +func WriteResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { + setCommonHeaders(w) + if mType != MimeNone { + w.Header().Set(hdrContentType, string(mType)) + } + w.Header().Set(hdrContentLength, strconv.Itoa(len(response))) + w.WriteHeader(statusCode) + if response == nil { + return + } + + _, _ = w.Write(response) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } +} + +// EncodeResponse encodes the response headers into XML format. +func EncodeResponse(response interface{}) []byte { + var bytesBuffer bytes.Buffer + bytesBuffer.WriteString(xml.Header) + _ = xml. + NewEncoder(&bytesBuffer). + Encode(response) + return bytesBuffer.Bytes() +} + +// EncodeToResponse encodes the response into ResponseWriter. +func EncodeToResponse(w http.ResponseWriter, response interface{}) error { + w.WriteHeader(http.StatusOK) + + if _, err := w.Write(xmlHeader); err != nil { + return err + } else if err = xml.NewEncoder(w).Encode(response); err != nil { + return err + } + + return nil +} + +// // WriteSuccessResponseXML writes success headers and response if any, +// // with content-type set to `application/xml`. +// func WriteSuccessResponseXML(w http.ResponseWriter, response []byte) { +// WriteResponse(w, http.StatusOK, response, MimeXML) +// } + +// WriteSuccessResponseHeadersOnly writes HTTP (200) OK response with no data +// to the client. +func WriteSuccessResponseHeadersOnly(w http.ResponseWriter) { + WriteResponse(w, http.StatusOK, nil, MimeNone) +} + // Error - Returns S3 error string. func (e ErrorResponse) Error() string { if e.Message == "" {