mirror of
https://github.com/minio/minio.git
synced 2024-12-25 22:55:54 -05:00
9a4d003ac7
The middleware sets up tracing, throttling, gzipped responses and collecting API stats. Additionally, this change updates the names of handler functions in metric labels to be the same as the name derived from Go lang reflection on the handler name. The metric api labels are now stored in memory the same as the handler name - they will be camelcased, e.g. `GetObject` instead of `getobject`. For compatibility, we lowercase the metric api label values when emitting the metrics.
682 lines
23 KiB
Go
682 lines
23 KiB
Go
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
|
|
consoleapi "github.com/minio/console/api"
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
"github.com/minio/mux"
|
|
"github.com/minio/pkg/v2/wildcard"
|
|
"github.com/rs/cors"
|
|
)
|
|
|
|
func newHTTPServerFn() *xhttp.Server {
|
|
globalObjLayerMutex.RLock()
|
|
defer globalObjLayerMutex.RUnlock()
|
|
return globalHTTPServer
|
|
}
|
|
|
|
func setHTTPServer(h *xhttp.Server) {
|
|
globalObjLayerMutex.Lock()
|
|
globalHTTPServer = h
|
|
globalObjLayerMutex.Unlock()
|
|
}
|
|
|
|
func newConsoleServerFn() *consoleapi.Server {
|
|
globalObjLayerMutex.RLock()
|
|
defer globalObjLayerMutex.RUnlock()
|
|
return globalConsoleSrv
|
|
}
|
|
|
|
func setConsoleSrv(srv *consoleapi.Server) {
|
|
globalObjLayerMutex.Lock()
|
|
globalConsoleSrv = srv
|
|
globalObjLayerMutex.Unlock()
|
|
}
|
|
|
|
func newObjectLayerFn() ObjectLayer {
|
|
globalObjLayerMutex.RLock()
|
|
defer globalObjLayerMutex.RUnlock()
|
|
return globalObjectAPI
|
|
}
|
|
|
|
func setObjectLayer(o ObjectLayer) {
|
|
globalObjLayerMutex.Lock()
|
|
globalObjectAPI = o
|
|
globalObjLayerMutex.Unlock()
|
|
}
|
|
|
|
// objectAPIHandler implements and provides http handlers for S3 API.
|
|
type objectAPIHandlers struct {
|
|
ObjectAPI func() ObjectLayer
|
|
}
|
|
|
|
// getHost tries its best to return the request host.
|
|
// According to section 14.23 of RFC 2616 the Host header
|
|
// can include the port number if the default value of 80 is not used.
|
|
func getHost(r *http.Request) string {
|
|
if r.URL.IsAbs() {
|
|
return r.URL.Host
|
|
}
|
|
return r.Host
|
|
}
|
|
|
|
func notImplementedHandler(w http.ResponseWriter, r *http.Request) {
|
|
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
|
}
|
|
|
|
type rejectedAPI struct {
|
|
api string
|
|
methods []string
|
|
queries []string
|
|
path string
|
|
}
|
|
|
|
var rejectedObjAPIs = []rejectedAPI{
|
|
{
|
|
api: "torrent",
|
|
methods: []string{http.MethodPut, http.MethodDelete, http.MethodGet},
|
|
queries: []string{"torrent", ""},
|
|
path: "/{object:.+}",
|
|
},
|
|
{
|
|
api: "acl",
|
|
methods: []string{http.MethodDelete},
|
|
queries: []string{"acl", ""},
|
|
path: "/{object:.+}",
|
|
},
|
|
}
|
|
|
|
var rejectedBucketAPIs = []rejectedAPI{
|
|
{
|
|
api: "inventory",
|
|
methods: []string{http.MethodGet, http.MethodPut, http.MethodDelete},
|
|
queries: []string{"inventory", ""},
|
|
},
|
|
{
|
|
api: "cors",
|
|
methods: []string{http.MethodPut, http.MethodDelete},
|
|
queries: []string{"cors", ""},
|
|
},
|
|
{
|
|
api: "metrics",
|
|
methods: []string{http.MethodGet, http.MethodPut, http.MethodDelete},
|
|
queries: []string{"metrics", ""},
|
|
},
|
|
{
|
|
api: "website",
|
|
methods: []string{http.MethodPut},
|
|
queries: []string{"website", ""},
|
|
},
|
|
{
|
|
api: "logging",
|
|
methods: []string{http.MethodPut, http.MethodDelete},
|
|
queries: []string{"logging", ""},
|
|
},
|
|
{
|
|
api: "accelerate",
|
|
methods: []string{http.MethodPut, http.MethodDelete},
|
|
queries: []string{"accelerate", ""},
|
|
},
|
|
{
|
|
api: "requestPayment",
|
|
methods: []string{http.MethodPut, http.MethodDelete},
|
|
queries: []string{"requestPayment", ""},
|
|
},
|
|
{
|
|
api: "acl",
|
|
methods: []string{http.MethodDelete, http.MethodPut, http.MethodHead},
|
|
queries: []string{"acl", ""},
|
|
},
|
|
{
|
|
api: "publicAccessBlock",
|
|
methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
|
|
queries: []string{"publicAccessBlock", ""},
|
|
},
|
|
{
|
|
api: "ownershipControls",
|
|
methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
|
|
queries: []string{"ownershipControls", ""},
|
|
},
|
|
{
|
|
api: "intelligent-tiering",
|
|
methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
|
|
queries: []string{"intelligent-tiering", ""},
|
|
},
|
|
{
|
|
api: "analytics",
|
|
methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
|
|
queries: []string{"analytics", ""},
|
|
},
|
|
}
|
|
|
|
// Set of s3 handler options as bit flags.
|
|
type s3HFlag uint8
|
|
|
|
const (
|
|
// when provided, disables Gzip compression.
|
|
noGZS3HFlag = 1 << iota
|
|
|
|
// when provided, enables only tracing of headers. Otherwise, both headers
|
|
// and body are traced.
|
|
traceHdrsS3HFlag
|
|
|
|
// when provided, disables throttling via the `maxClients` middleware.
|
|
noThrottleS3HFlag
|
|
)
|
|
|
|
func (h s3HFlag) has(flag s3HFlag) bool {
|
|
// Use bitwise-AND and check if the result is non-zero.
|
|
return h&flag != 0
|
|
}
|
|
|
|
// s3APIMiddleware - performs some common handler functionality for S3 API
|
|
// handlers.
|
|
//
|
|
// It is set per-"handler function registration" in the router to allow for
|
|
// behavior modification via flags.
|
|
//
|
|
// This middleware always calls `collectAPIStats` to collect API stats.
|
|
//
|
|
// The passed in handler function must be a method of `objectAPIHandlers` for
|
|
// the name displayed in logs and trace to be accurate. The name is extracted
|
|
// via reflection.
|
|
//
|
|
// When **no** flags are passed, the behavior is to trace both headers and body,
|
|
// gzip the response and throttle the handler via `maxClients`. Each of these
|
|
// can be disabled via the corresponding `s3HFlag`.
|
|
//
|
|
// CAUTION: for requests involving large req/resp bodies ensure to pass the
|
|
// `traceHdrsS3HFlag`, otherwise both headers and body will be traced, causing
|
|
// high memory usage!
|
|
func s3APIMiddleware(f http.HandlerFunc, flags ...s3HFlag) http.HandlerFunc {
|
|
// Collect all flags with bitwise-OR and assign operator
|
|
var handlerFlags s3HFlag
|
|
for _, flag := range flags {
|
|
handlerFlags |= flag
|
|
}
|
|
|
|
// Get name of the handler using reflection.
|
|
handlerName := getHandlerName(f, "objectAPIHandlers")
|
|
|
|
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
|
// Wrap the actual handler with the appropriate tracing middleware.
|
|
var tracedHandler http.HandlerFunc
|
|
if handlerFlags.has(traceHdrsS3HFlag) {
|
|
tracedHandler = httpTraceHdrs(f)
|
|
} else {
|
|
tracedHandler = httpTraceAll(f)
|
|
}
|
|
|
|
// Skip wrapping with the gzip middleware if specified.
|
|
var gzippedHandler http.HandlerFunc = tracedHandler
|
|
if !handlerFlags.has(noGZS3HFlag) {
|
|
gzippedHandler = gzipHandler(gzippedHandler)
|
|
}
|
|
|
|
// Skip wrapping with throttling middleware if specified.
|
|
var throttledHandler http.HandlerFunc = gzippedHandler
|
|
if !handlerFlags.has(noThrottleS3HFlag) {
|
|
throttledHandler = maxClients(throttledHandler)
|
|
}
|
|
|
|
// Collect API stats using the API name got from reflection in
|
|
// `getHandlerName`.
|
|
statsCollectedHandler := collectAPIStats(handlerName, throttledHandler)
|
|
|
|
// Call the final handler.
|
|
statsCollectedHandler(w, r)
|
|
}
|
|
|
|
return handler
|
|
}
|
|
|
|
// registerAPIRouter - registers S3 compatible APIs.
|
|
func registerAPIRouter(router *mux.Router) {
|
|
// Initialize API.
|
|
api := objectAPIHandlers{
|
|
ObjectAPI: newObjectLayerFn,
|
|
}
|
|
|
|
// API Router
|
|
apiRouter := router.PathPrefix(SlashSeparator).Subrouter()
|
|
|
|
var routers []*mux.Router
|
|
for _, domainName := range globalDomainNames {
|
|
if IsKubernetes() {
|
|
routers = append(routers, apiRouter.MatcherFunc(func(r *http.Request, match *mux.RouteMatch) bool {
|
|
host, _, err := net.SplitHostPort(getHost(r))
|
|
if err != nil {
|
|
host = r.Host
|
|
}
|
|
// Make sure to skip matching minio.<domain>` this is
|
|
// specifically meant for operator/k8s deployment
|
|
// The reason we need to skip this is for a special
|
|
// usecase where we need to make sure that
|
|
// minio.<namespace>.svc.<cluster_domain> is ignored
|
|
// by the bucketDNS style to ensure that path style
|
|
// is available and honored at this domain.
|
|
//
|
|
// All other `<bucket>.<namespace>.svc.<cluster_domain>`
|
|
// makes sure that buckets are routed through this matcher
|
|
// to match for `<bucket>`
|
|
return host != minioReservedBucket+"."+domainName
|
|
}).Host("{bucket:.+}."+domainName).Subrouter())
|
|
} else {
|
|
routers = append(routers, apiRouter.Host("{bucket:.+}."+domainName).Subrouter())
|
|
}
|
|
}
|
|
routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
|
|
|
|
for _, router := range routers {
|
|
// Register all rejected object APIs
|
|
for _, r := range rejectedObjAPIs {
|
|
t := router.Methods(r.methods...).
|
|
HandlerFunc(collectAPIStats(r.api, httpTraceAll(notImplementedHandler))).
|
|
Queries(r.queries...)
|
|
t.Path(r.path)
|
|
}
|
|
|
|
// Object operations
|
|
// HeadObject
|
|
router.Methods(http.MethodHead).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.HeadObjectHandler))
|
|
|
|
// GetObjectAttributes
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectAttributesHandler, traceHdrsS3HFlag)).
|
|
Queries("attributes", "")
|
|
|
|
// CopyObjectPart
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").
|
|
HandlerFunc(s3APIMiddleware(api.CopyObjectPartHandler)).
|
|
Queries("partNumber", "{partNumber:.*}", "uploadId", "{uploadId:.*}")
|
|
// PutObjectPart
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectPartHandler, traceHdrsS3HFlag)).
|
|
Queries("partNumber", "{partNumber:.*}", "uploadId", "{uploadId:.*}")
|
|
// ListObjectParts
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectPartsHandler)).
|
|
Queries("uploadId", "{uploadId:.*}")
|
|
// CompleteMultipartUpload
|
|
router.Methods(http.MethodPost).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.CompleteMultipartUploadHandler)).
|
|
Queries("uploadId", "{uploadId:.*}")
|
|
// NewMultipartUpload
|
|
router.Methods(http.MethodPost).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.NewMultipartUploadHandler)).
|
|
Queries("uploads", "")
|
|
// AbortMultipartUpload
|
|
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.AbortMultipartUploadHandler)).
|
|
Queries("uploadId", "{uploadId:.*}")
|
|
// GetObjectACL - this is a dummy call.
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectACLHandler, traceHdrsS3HFlag)).
|
|
Queries("acl", "")
|
|
// PutObjectACL - this is a dummy call.
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectACLHandler, traceHdrsS3HFlag)).
|
|
Queries("acl", "")
|
|
// GetObjectTagging
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectTaggingHandler, traceHdrsS3HFlag)).
|
|
Queries("tagging", "")
|
|
// PutObjectTagging
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectTaggingHandler, traceHdrsS3HFlag)).
|
|
Queries("tagging", "")
|
|
// DeleteObjectTagging
|
|
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.DeleteObjectTaggingHandler, traceHdrsS3HFlag)).
|
|
Queries("tagging", "")
|
|
// SelectObjectContent
|
|
router.Methods(http.MethodPost).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.SelectObjectContentHandler, traceHdrsS3HFlag)).
|
|
Queries("select", "").Queries("select-type", "2")
|
|
// GetObjectRetention
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectRetentionHandler)).
|
|
Queries("retention", "")
|
|
// GetObjectLegalHold
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectLegalHoldHandler)).
|
|
Queries("legal-hold", "")
|
|
// GetObject with lambda ARNs
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectLambdaHandler, traceHdrsS3HFlag)).
|
|
Queries("lambdaArn", "{lambdaArn:.*}")
|
|
// GetObject
|
|
router.Methods(http.MethodGet).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.GetObjectHandler, traceHdrsS3HFlag))
|
|
// CopyObject
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").
|
|
HandlerFunc(s3APIMiddleware(api.CopyObjectHandler))
|
|
// PutObjectRetention
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectRetentionHandler)).
|
|
Queries("retention", "")
|
|
// PutObjectLegalHold
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectLegalHoldHandler)).
|
|
Queries("legal-hold", "")
|
|
|
|
// PutObject with auto-extract support for zip
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HeadersRegexp(xhttp.AmzSnowballExtract, "true").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectExtractHandler, traceHdrsS3HFlag))
|
|
|
|
// PutObject
|
|
router.Methods(http.MethodPut).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PutObjectHandler, traceHdrsS3HFlag))
|
|
|
|
// DeleteObject
|
|
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.DeleteObjectHandler))
|
|
|
|
// PostRestoreObject
|
|
router.Methods(http.MethodPost).Path("/{object:.+}").
|
|
HandlerFunc(s3APIMiddleware(api.PostRestoreObjectHandler)).
|
|
Queries("restore", "")
|
|
|
|
// Bucket operations
|
|
|
|
// GetBucketLocation
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketLocationHandler)).
|
|
Queries("location", "")
|
|
// GetBucketPolicy
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketPolicyHandler)).
|
|
Queries("policy", "")
|
|
// GetBucketLifecycle
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketLifecycleHandler)).
|
|
Queries("lifecycle", "")
|
|
// GetBucketEncryption
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketEncryptionHandler)).
|
|
Queries("encryption", "")
|
|
// GetBucketObjectLockConfig
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketObjectLockConfigHandler)).
|
|
Queries("object-lock", "")
|
|
// GetBucketReplicationConfig
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationConfigHandler)).
|
|
Queries("replication", "")
|
|
// GetBucketVersioning
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketVersioningHandler)).
|
|
Queries("versioning", "")
|
|
// GetBucketNotification
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketNotificationHandler)).
|
|
Queries("notification", "")
|
|
// ListenNotification
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListenNotificationHandler, noThrottleS3HFlag)).
|
|
Queries("events", "{events:.*}")
|
|
// ResetBucketReplicationStatus - MinIO extension API
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ResetBucketReplicationStatusHandler)).
|
|
Queries("replication-reset-status", "")
|
|
|
|
// Dummy Bucket Calls
|
|
// GetBucketACL -- this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketACLHandler)).
|
|
Queries("acl", "")
|
|
// PutBucketACL -- this is a dummy call.
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketACLHandler)).
|
|
Queries("acl", "")
|
|
// GetBucketCors - this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketCorsHandler)).
|
|
Queries("cors", "")
|
|
// GetBucketWebsiteHandler - this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketWebsiteHandler)).
|
|
Queries("website", "")
|
|
// GetBucketAccelerateHandler - this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketAccelerateHandler)).
|
|
Queries("accelerate", "")
|
|
// GetBucketRequestPaymentHandler - this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketRequestPaymentHandler)).
|
|
Queries("requestPayment", "")
|
|
// GetBucketLoggingHandler - this is a dummy call.
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketLoggingHandler)).
|
|
Queries("logging", "")
|
|
// GetBucketTaggingHandler
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketTaggingHandler)).
|
|
Queries("tagging", "")
|
|
// DeleteBucketWebsiteHandler
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketWebsiteHandler)).
|
|
Queries("website", "")
|
|
// DeleteBucketTaggingHandler
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketTaggingHandler)).
|
|
Queries("tagging", "")
|
|
|
|
// ListMultipartUploads
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListMultipartUploadsHandler)).
|
|
Queries("uploads", "")
|
|
// ListObjectsV2M
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectsV2MHandler)).
|
|
Queries("list-type", "2", "metadata", "true")
|
|
// ListObjectsV2
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectsV2Handler)).
|
|
Queries("list-type", "2")
|
|
// ListObjectVersions
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectVersionsMHandler)).
|
|
Queries("versions", "", "metadata", "true")
|
|
// ListObjectVersions
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectVersionsHandler)).
|
|
Queries("versions", "")
|
|
// GetBucketPolicyStatus
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketPolicyStatusHandler)).
|
|
Queries("policyStatus", "")
|
|
// PutBucketLifecycle
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketLifecycleHandler)).
|
|
Queries("lifecycle", "")
|
|
// PutBucketReplicationConfig
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketReplicationConfigHandler)).
|
|
Queries("replication", "")
|
|
// PutBucketEncryption
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketEncryptionHandler)).
|
|
Queries("encryption", "")
|
|
|
|
// PutBucketPolicy
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketPolicyHandler)).
|
|
Queries("policy", "")
|
|
|
|
// PutBucketObjectLockConfig
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketObjectLockConfigHandler)).
|
|
Queries("object-lock", "")
|
|
// PutBucketTaggingHandler
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketTaggingHandler)).
|
|
Queries("tagging", "")
|
|
// PutBucketVersioning
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketVersioningHandler)).
|
|
Queries("versioning", "")
|
|
// PutBucketNotification
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketNotificationHandler)).
|
|
Queries("notification", "")
|
|
// ResetBucketReplicationStart - MinIO extension API
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.ResetBucketReplicationStartHandler)).
|
|
Queries("replication-reset", "")
|
|
|
|
// PutBucket
|
|
router.Methods(http.MethodPut).
|
|
HandlerFunc(s3APIMiddleware(api.PutBucketHandler))
|
|
// HeadBucket
|
|
router.Methods(http.MethodHead).
|
|
HandlerFunc(s3APIMiddleware(api.HeadBucketHandler))
|
|
// PostPolicy
|
|
router.Methods(http.MethodPost).
|
|
MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool {
|
|
return isRequestPostPolicySignatureV4(r)
|
|
}).
|
|
HandlerFunc(s3APIMiddleware(api.PostPolicyBucketHandler, traceHdrsS3HFlag))
|
|
// DeleteMultipleObjects
|
|
router.Methods(http.MethodPost).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteMultipleObjectsHandler)).
|
|
Queries("delete", "")
|
|
// DeleteBucketPolicy
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketPolicyHandler)).
|
|
Queries("policy", "")
|
|
// DeleteBucketReplication
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketReplicationConfigHandler)).
|
|
Queries("replication", "")
|
|
// DeleteBucketLifecycle
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketLifecycleHandler)).
|
|
Queries("lifecycle", "")
|
|
// DeleteBucketEncryption
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketEncryptionHandler)).
|
|
Queries("encryption", "")
|
|
// DeleteBucket
|
|
router.Methods(http.MethodDelete).
|
|
HandlerFunc(s3APIMiddleware(api.DeleteBucketHandler))
|
|
|
|
// MinIO extension API for replication.
|
|
//
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationMetricsV2Handler)).
|
|
Queries("replication-metrics", "2")
|
|
// deprecated handler
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationMetricsHandler)).
|
|
Queries("replication-metrics", "")
|
|
|
|
// ValidateBucketReplicationCreds
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ValidateBucketReplicationCredsHandler)).
|
|
Queries("replication-check", "")
|
|
|
|
// Register rejected bucket APIs
|
|
for _, r := range rejectedBucketAPIs {
|
|
router.Methods(r.methods...).
|
|
HandlerFunc(collectAPIStats(r.api, httpTraceAll(notImplementedHandler))).
|
|
Queries(r.queries...)
|
|
}
|
|
|
|
// S3 ListObjectsV1 (Legacy)
|
|
router.Methods(http.MethodGet).
|
|
HandlerFunc(s3APIMiddleware(api.ListObjectsV1Handler))
|
|
}
|
|
|
|
// Root operation
|
|
|
|
// ListenNotification
|
|
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).
|
|
HandlerFunc(s3APIMiddleware(api.ListenNotificationHandler, noThrottleS3HFlag)).
|
|
Queries("events", "{events:.*}")
|
|
|
|
// ListBuckets
|
|
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).
|
|
HandlerFunc(s3APIMiddleware(api.ListBucketsHandler))
|
|
|
|
// S3 browser with signature v4 adds '//' for ListBuckets request, so rather
|
|
// than failing with UnknownAPIRequest we simply handle it for now.
|
|
apiRouter.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).
|
|
HandlerFunc(s3APIMiddleware(api.ListBucketsHandler))
|
|
|
|
// If none of the routes match add default error handler routes
|
|
apiRouter.NotFoundHandler = collectAPIStats("notfound", httpTraceAll(errorResponseHandler))
|
|
apiRouter.MethodNotAllowedHandler = collectAPIStats("methodnotallowed", httpTraceAll(methodNotAllowedHandler("S3")))
|
|
}
|
|
|
|
// corsHandler handler for CORS (Cross Origin Resource Sharing)
|
|
func corsHandler(handler http.Handler) http.Handler {
|
|
commonS3Headers := []string{
|
|
xhttp.Date,
|
|
xhttp.ETag,
|
|
xhttp.ServerInfo,
|
|
xhttp.Connection,
|
|
xhttp.AcceptRanges,
|
|
xhttp.ContentRange,
|
|
xhttp.ContentEncoding,
|
|
xhttp.ContentLength,
|
|
xhttp.ContentType,
|
|
xhttp.ContentDisposition,
|
|
xhttp.LastModified,
|
|
xhttp.ContentLanguage,
|
|
xhttp.CacheControl,
|
|
xhttp.RetryAfter,
|
|
xhttp.AmzBucketRegion,
|
|
xhttp.Expires,
|
|
"X-Amz*",
|
|
"x-amz*",
|
|
"*",
|
|
}
|
|
opts := cors.Options{
|
|
AllowOriginFunc: func(origin string) bool {
|
|
for _, allowedOrigin := range globalAPIConfig.getCorsAllowOrigins() {
|
|
if wildcard.MatchSimple(allowedOrigin, origin) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
},
|
|
AllowedMethods: []string{
|
|
http.MethodGet,
|
|
http.MethodPut,
|
|
http.MethodHead,
|
|
http.MethodPost,
|
|
http.MethodDelete,
|
|
http.MethodOptions,
|
|
http.MethodPatch,
|
|
},
|
|
AllowedHeaders: commonS3Headers,
|
|
ExposedHeaders: commonS3Headers,
|
|
AllowCredentials: true,
|
|
}
|
|
return cors.New(opts).Handler(handler)
|
|
}
|