storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/api-router.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"net"
    21  	"net/http"
    22  
    23  	"github.com/gorilla/mux"
    24  	"github.com/rs/cors"
    25  
    26  	xhttp "storj.io/minio/cmd/http"
    27  	"storj.io/minio/pkg/wildcard"
    28  )
    29  
    30  func newHTTPServerFn() *xhttp.Server {
    31  	globalObjLayerMutex.RLock()
    32  	defer globalObjLayerMutex.RUnlock()
    33  	return globalHTTPServer
    34  }
    35  
    36  func setHTTPServer(h *xhttp.Server) {
    37  	globalObjLayerMutex.Lock()
    38  	globalHTTPServer = h
    39  	globalObjLayerMutex.Unlock()
    40  }
    41  
    42  func newObjectLayerFn() ObjectLayer {
    43  	globalObjLayerMutex.RLock()
    44  	defer globalObjLayerMutex.RUnlock()
    45  	return globalObjectAPI
    46  }
    47  
    48  func newCachedObjectLayerFn() CacheObjectLayer {
    49  	globalObjLayerMutex.RLock()
    50  	defer globalObjLayerMutex.RUnlock()
    51  	return globalCacheObjectAPI
    52  }
    53  
    54  func setCacheObjectLayer(c CacheObjectLayer) {
    55  	globalObjLayerMutex.Lock()
    56  	globalCacheObjectAPI = c
    57  	globalObjLayerMutex.Unlock()
    58  }
    59  
    60  func SetObjectLayer(o ObjectLayer) {
    61  	globalObjLayerMutex.Lock()
    62  	globalObjectAPI = o
    63  	globalObjLayerMutex.Unlock()
    64  }
    65  
    66  // ObjectAPIHandler implements and provides http handlers for S3 API.
    67  type ObjectAPIHandlers struct {
    68  	ObjectAPI func() ObjectLayer
    69  	CacheAPI  func() CacheObjectLayer
    70  }
    71  
    72  // getHost tries its best to return the request host.
    73  // According to section 14.23 of RFC 2616 the Host header
    74  // can include the port number if the default value of 80 is not used.
    75  func getHost(r *http.Request) string {
    76  	if r.URL.IsAbs() {
    77  		return r.URL.Host
    78  	}
    79  	return r.Host
    80  }
    81  
    82  func notImplementedHandler(w http.ResponseWriter, r *http.Request) {
    83  	WriteErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
    84  }
    85  
    86  type rejectedAPI struct {
    87  	api     string
    88  	methods []string
    89  	queries []string
    90  	path    string
    91  }
    92  
    93  var rejectedAPIs = []rejectedAPI{
    94  	{
    95  		api:     "inventory",
    96  		methods: []string{http.MethodGet, http.MethodPut, http.MethodDelete},
    97  		queries: []string{"inventory", ""},
    98  	},
    99  	{
   100  		api:     "cors",
   101  		methods: []string{http.MethodPut, http.MethodDelete},
   102  		queries: []string{"cors", ""},
   103  	},
   104  	{
   105  		api:     "metrics",
   106  		methods: []string{http.MethodGet, http.MethodPut, http.MethodDelete},
   107  		queries: []string{"metrics", ""},
   108  	},
   109  	{
   110  		api:     "website",
   111  		methods: []string{http.MethodPut},
   112  		queries: []string{"website", ""},
   113  	},
   114  	{
   115  		api:     "logging",
   116  		methods: []string{http.MethodPut, http.MethodDelete},
   117  		queries: []string{"logging", ""},
   118  	},
   119  	{
   120  		api:     "accelerate",
   121  		methods: []string{http.MethodPut, http.MethodDelete},
   122  		queries: []string{"accelerate", ""},
   123  	},
   124  	{
   125  		api:     "requestPayment",
   126  		methods: []string{http.MethodPut, http.MethodDelete},
   127  		queries: []string{"requestPayment", ""},
   128  	},
   129  	{
   130  		api:     "torrent",
   131  		methods: []string{http.MethodPut, http.MethodDelete, http.MethodGet},
   132  		queries: []string{"torrent", ""},
   133  		path:    "/{object:.+}",
   134  	},
   135  	{
   136  		api:     "acl",
   137  		methods: []string{http.MethodDelete},
   138  		queries: []string{"acl", ""},
   139  		path:    "/{object:.+}",
   140  	},
   141  	{
   142  		api:     "acl",
   143  		methods: []string{http.MethodDelete, http.MethodPut, http.MethodHead},
   144  		queries: []string{"acl", ""},
   145  	},
   146  	{
   147  		api:     "publicAccessBlock",
   148  		methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
   149  		queries: []string{"publicAccessBlock", ""},
   150  	},
   151  	{
   152  		api:     "ownershipControls",
   153  		methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
   154  		queries: []string{"ownershipControls", ""},
   155  	},
   156  	{
   157  		api:     "intelligent-tiering",
   158  		methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
   159  		queries: []string{"intelligent-tiering", ""},
   160  	},
   161  	{
   162  		api:     "analytics",
   163  		methods: []string{http.MethodDelete, http.MethodPut, http.MethodGet},
   164  		queries: []string{"analytics", ""},
   165  	},
   166  }
   167  
   168  func rejectUnsupportedAPIs(router *mux.Router) {
   169  	for _, r := range rejectedAPIs {
   170  		t := router.Methods(r.methods...).
   171  			HandlerFunc(CollectAPIStats(r.api, HTTPTraceAll(notImplementedHandler))).
   172  			Queries(r.queries...)
   173  		if r.path != "" {
   174  			t.Path(r.path)
   175  		}
   176  	}
   177  }
   178  
   179  // registerAPIRouter - registers S3 compatible APIs.
   180  func registerAPIRouter(router *mux.Router) {
   181  	// Initialize API.
   182  	api := ObjectAPIHandlers{
   183  		ObjectAPI: newObjectLayerFn,
   184  		CacheAPI:  newCachedObjectLayerFn,
   185  	}
   186  
   187  	// API Router
   188  	apiRouter := router.PathPrefix(SlashSeparator).Subrouter()
   189  
   190  	var routers []*mux.Router
   191  	for _, domainName := range globalDomainNames {
   192  		if IsKubernetes() {
   193  			routers = append(routers, apiRouter.MatcherFunc(func(r *http.Request, match *mux.RouteMatch) bool {
   194  				host, _, err := net.SplitHostPort(getHost(r))
   195  				if err != nil {
   196  					host = r.Host
   197  				}
   198  				// Make sure to skip matching minio.<domain>` this is
   199  				// specifically meant for operator/k8s deployment
   200  				// The reason we need to skip this is for a special
   201  				// usecase where we need to make sure that
   202  				// minio.<namespace>.svc.<cluster_domain> is ignored
   203  				// by the bucketDNS style to ensure that path style
   204  				// is available and honored at this domain.
   205  				//
   206  				// All other `<bucket>.<namespace>.svc.<cluster_domain>`
   207  				// makes sure that buckets are routed through this matcher
   208  				// to match for `<bucket>`
   209  				return host != minioReservedBucket+"."+domainName
   210  			}).Host("{bucket:.+}."+domainName).Subrouter())
   211  		} else {
   212  			routers = append(routers, apiRouter.Host("{bucket:.+}."+domainName).Subrouter())
   213  		}
   214  	}
   215  	routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
   216  
   217  	for _, router := range routers {
   218  		rejectUnsupportedAPIs(router)
   219  		// Object operations
   220  		// HeadObject
   221  		router.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(
   222  			CollectAPIStats("headobject", MaxClients(HTTPTraceAll(api.HeadObjectHandler))))
   223  		// CopyObjectPart
   224  		router.Methods(http.MethodPut).Path("/{object:.+}").
   225  			HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").
   226  			HandlerFunc(CollectAPIStats("copyobjectpart", MaxClients(HTTPTraceAll(api.CopyObjectPartHandler)))).
   227  			Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
   228  		// PutObjectPart
   229  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   230  			CollectAPIStats("putobjectpart", MaxClients(HTTPTraceHdrs(api.PutObjectPartHandler)))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
   231  		// ListObjectParts
   232  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   233  			CollectAPIStats("listobjectparts", MaxClients(HTTPTraceAll(api.ListObjectPartsHandler)))).Queries("uploadId", "{uploadId:.*}")
   234  		// CompleteMultipartUpload
   235  		router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
   236  			CollectAPIStats("completemutipartupload", MaxClients(HTTPTraceAll(api.CompleteMultipartUploadHandler)))).Queries("uploadId", "{uploadId:.*}")
   237  		// NewMultipartUpload
   238  		router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
   239  			CollectAPIStats("newmultipartupload", MaxClients(HTTPTraceAll(api.NewMultipartUploadHandler)))).Queries("uploads", "")
   240  		// AbortMultipartUpload
   241  		router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
   242  			CollectAPIStats("abortmultipartupload", MaxClients(HTTPTraceAll(api.AbortMultipartUploadHandler)))).Queries("uploadId", "{uploadId:.*}")
   243  		// GetObjectACL - this is a dummy call.
   244  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   245  			CollectAPIStats("getobjectacl", MaxClients(HTTPTraceHdrs(api.GetObjectACLHandler)))).Queries("acl", "")
   246  		// PutObjectACL - this is a dummy call.
   247  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   248  			CollectAPIStats("putobjectacl", MaxClients(HTTPTraceHdrs(api.PutObjectACLHandler)))).Queries("acl", "")
   249  		// GetObjectTagging
   250  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   251  			CollectAPIStats("getobjecttagging", MaxClients(HTTPTraceHdrs(api.GetObjectTaggingHandler)))).Queries("tagging", "")
   252  		// PutObjectTagging
   253  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   254  			CollectAPIStats("putobjecttagging", MaxClients(HTTPTraceHdrs(api.PutObjectTaggingHandler)))).Queries("tagging", "")
   255  		// DeleteObjectTagging
   256  		router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
   257  			CollectAPIStats("deleteobjecttagging", MaxClients(HTTPTraceHdrs(api.DeleteObjectTaggingHandler)))).Queries("tagging", "")
   258  		// SelectObjectContent
   259  		router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
   260  			CollectAPIStats("selectobjectcontent", MaxClients(HTTPTraceHdrs(api.SelectObjectContentHandler)))).Queries("select", "").Queries("select-type", "2")
   261  		// GetObjectRetention
   262  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   263  			CollectAPIStats("getobjectretention", MaxClients(HTTPTraceAll(api.GetObjectRetentionHandler)))).Queries("retention", "")
   264  		// GetObjectLegalHold
   265  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   266  			CollectAPIStats("getobjectlegalhold", MaxClients(HTTPTraceAll(api.GetObjectLegalHoldHandler)))).Queries("legal-hold", "")
   267  		// GetObject
   268  		router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
   269  			CollectAPIStats("getobject", MaxClients(HTTPTraceHdrs(api.GetObjectHandler))))
   270  		// CopyObject
   271  		router.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(
   272  			CollectAPIStats("copyobject", MaxClients(HTTPTraceAll(api.CopyObjectHandler))))
   273  		// PutObjectRetention
   274  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   275  			CollectAPIStats("putobjectretention", MaxClients(HTTPTraceAll(api.PutObjectRetentionHandler)))).Queries("retention", "")
   276  		// PutObjectLegalHold
   277  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   278  			CollectAPIStats("putobjectlegalhold", MaxClients(HTTPTraceAll(api.PutObjectLegalHoldHandler)))).Queries("legal-hold", "")
   279  
   280  		// PutObject with auto-extract support for zip
   281  		router.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzSnowballExtract, "true").HandlerFunc(
   282  			CollectAPIStats("putobject", MaxClients(HTTPTraceHdrs(api.PutObjectExtractHandler))))
   283  
   284  		// PutObject
   285  		router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
   286  			CollectAPIStats("putobject", MaxClients(HTTPTraceHdrs(api.PutObjectHandler))))
   287  
   288  		// DeleteObject
   289  		router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
   290  			CollectAPIStats("deleteobject", MaxClients(HTTPTraceAll(api.DeleteObjectHandler))))
   291  
   292  		// PostRestoreObject
   293  		router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
   294  			CollectAPIStats("restoreobject", MaxClients(HTTPTraceAll(api.PostRestoreObjectHandler)))).Queries("restore", "")
   295  
   296  		/// Bucket operations
   297  		// GetBucketLocation
   298  		router.Methods(http.MethodGet).HandlerFunc(
   299  			CollectAPIStats("getbucketlocation", MaxClients(HTTPTraceAll(api.GetBucketLocationHandler)))).Queries("location", "")
   300  		// GetBucketPolicy
   301  		router.Methods(http.MethodGet).HandlerFunc(
   302  			CollectAPIStats("getbucketpolicy", MaxClients(HTTPTraceAll(api.GetBucketPolicyHandler)))).Queries("policy", "")
   303  		// GetBucketLifecycle
   304  		router.Methods(http.MethodGet).HandlerFunc(
   305  			CollectAPIStats("getbucketlifecycle", MaxClients(HTTPTraceAll(api.GetBucketLifecycleHandler)))).Queries("lifecycle", "")
   306  		// GetBucketEncryption
   307  		router.Methods(http.MethodGet).HandlerFunc(
   308  			CollectAPIStats("getbucketencryption", MaxClients(HTTPTraceAll(api.GetBucketEncryptionHandler)))).Queries("encryption", "")
   309  		// GetBucketObjectLockConfig
   310  		router.Methods(http.MethodGet).HandlerFunc(
   311  			CollectAPIStats("getbucketobjectlockconfiguration", MaxClients(HTTPTraceAll(api.GetBucketObjectLockConfigHandler)))).Queries("object-lock", "")
   312  		// GetBucketReplicationConfig
   313  		router.Methods(http.MethodGet).HandlerFunc(
   314  			CollectAPIStats("getbucketreplicationconfiguration", MaxClients(HTTPTraceAll(api.GetBucketReplicationConfigHandler)))).Queries("replication", "")
   315  		// GetBucketVersioning
   316  		router.Methods(http.MethodGet).HandlerFunc(
   317  			CollectAPIStats("getbucketversioning", MaxClients(HTTPTraceAll(api.GetBucketVersioningHandler)))).Queries("versioning", "")
   318  		// GetBucketNotification
   319  		router.Methods(http.MethodGet).HandlerFunc(
   320  			CollectAPIStats("getbucketnotification", MaxClients(HTTPTraceAll(api.GetBucketNotificationHandler)))).Queries("notification", "")
   321  		// ListenNotification
   322  		router.Methods(http.MethodGet).HandlerFunc(
   323  			CollectAPIStats("listennotification", MaxClients(HTTPTraceAll(api.ListenNotificationHandler)))).Queries("events", "{events:.*}")
   324  
   325  		// Dummy Bucket Calls
   326  		// GetBucketACL -- this is a dummy call.
   327  		router.Methods(http.MethodGet).HandlerFunc(
   328  			CollectAPIStats("getbucketacl", MaxClients(HTTPTraceAll(api.GetBucketACLHandler)))).Queries("acl", "")
   329  		// PutBucketACL -- this is a dummy call.
   330  		router.Methods(http.MethodPut).HandlerFunc(
   331  			CollectAPIStats("putbucketacl", MaxClients(HTTPTraceAll(api.PutBucketACLHandler)))).Queries("acl", "")
   332  		// GetBucketCors - this is a dummy call.
   333  		router.Methods(http.MethodGet).HandlerFunc(
   334  			CollectAPIStats("getbucketcors", MaxClients(HTTPTraceAll(api.GetBucketCorsHandler)))).Queries("cors", "")
   335  		// GetBucketWebsiteHandler - this is a dummy call.
   336  		router.Methods(http.MethodGet).HandlerFunc(
   337  			CollectAPIStats("getbucketwebsite", MaxClients(HTTPTraceAll(api.GetBucketWebsiteHandler)))).Queries("website", "")
   338  		// GetBucketAccelerateHandler - this is a dummy call.
   339  		router.Methods(http.MethodGet).HandlerFunc(
   340  			CollectAPIStats("getbucketaccelerate", MaxClients(HTTPTraceAll(api.GetBucketAccelerateHandler)))).Queries("accelerate", "")
   341  		// GetBucketRequestPaymentHandler - this is a dummy call.
   342  		router.Methods(http.MethodGet).HandlerFunc(
   343  			CollectAPIStats("getbucketrequestpayment", MaxClients(HTTPTraceAll(api.GetBucketRequestPaymentHandler)))).Queries("requestPayment", "")
   344  		// GetBucketLoggingHandler - this is a dummy call.
   345  		router.Methods(http.MethodGet).HandlerFunc(
   346  			CollectAPIStats("getbucketlogging", MaxClients(HTTPTraceAll(api.GetBucketLoggingHandler)))).Queries("logging", "")
   347  		// GetBucketTaggingHandler
   348  		router.Methods(http.MethodGet).HandlerFunc(
   349  			CollectAPIStats("getbuckettagging", MaxClients(HTTPTraceAll(api.GetBucketTaggingHandler)))).Queries("tagging", "")
   350  		//DeleteBucketWebsiteHandler
   351  		router.Methods(http.MethodDelete).HandlerFunc(
   352  			CollectAPIStats("deletebucketwebsite", MaxClients(HTTPTraceAll(api.DeleteBucketWebsiteHandler)))).Queries("website", "")
   353  		// DeleteBucketTaggingHandler
   354  		router.Methods(http.MethodDelete).HandlerFunc(
   355  			CollectAPIStats("deletebuckettagging", MaxClients(HTTPTraceAll(api.DeleteBucketTaggingHandler)))).Queries("tagging", "")
   356  
   357  		// ListMultipartUploads
   358  		router.Methods(http.MethodGet).HandlerFunc(
   359  			CollectAPIStats("listmultipartuploads", MaxClients(HTTPTraceAll(api.ListMultipartUploadsHandler)))).Queries("uploads", "")
   360  		// ListObjectsV2M
   361  		router.Methods(http.MethodGet).HandlerFunc(
   362  			CollectAPIStats("listobjectsv2M", MaxClients(HTTPTraceAll(api.ListObjectsV2MHandler)))).Queries("list-type", "2", "metadata", "true")
   363  		// ListObjectsV2
   364  		router.Methods(http.MethodGet).HandlerFunc(
   365  			CollectAPIStats("listobjectsv2", MaxClients(HTTPTraceAll(api.ListObjectsV2Handler)))).Queries("list-type", "2")
   366  		// ListObjectVersions
   367  		router.Methods(http.MethodGet).HandlerFunc(
   368  			CollectAPIStats("listobjectversions", MaxClients(HTTPTraceAll(api.ListObjectVersionsHandler)))).Queries("versions", "")
   369  		// GetBucketPolicyStatus
   370  		router.Methods(http.MethodGet).HandlerFunc(
   371  			CollectAPIStats("getpolicystatus", MaxClients(HTTPTraceAll(api.GetBucketPolicyStatusHandler)))).Queries("policyStatus", "")
   372  		// PutBucketLifecycle
   373  		router.Methods(http.MethodPut).HandlerFunc(
   374  			CollectAPIStats("putbucketlifecycle", MaxClients(HTTPTraceAll(api.PutBucketLifecycleHandler)))).Queries("lifecycle", "")
   375  		// PutBucketReplicationConfig
   376  		router.Methods(http.MethodPut).HandlerFunc(
   377  			CollectAPIStats("putbucketreplicationconfiguration", MaxClients(HTTPTraceAll(api.PutBucketReplicationConfigHandler)))).Queries("replication", "")
   378  		// PutBucketEncryption
   379  		router.Methods(http.MethodPut).HandlerFunc(
   380  			CollectAPIStats("putbucketencryption", MaxClients(HTTPTraceAll(api.PutBucketEncryptionHandler)))).Queries("encryption", "")
   381  
   382  		// PutBucketPolicy
   383  		router.Methods(http.MethodPut).HandlerFunc(
   384  			CollectAPIStats("putbucketpolicy", MaxClients(HTTPTraceAll(api.PutBucketPolicyHandler)))).Queries("policy", "")
   385  
   386  		// PutBucketObjectLockConfig
   387  		router.Methods(http.MethodPut).HandlerFunc(
   388  			CollectAPIStats("putbucketobjectlockconfig", MaxClients(HTTPTraceAll(api.PutBucketObjectLockConfigHandler)))).Queries("object-lock", "")
   389  		// PutBucketTaggingHandler
   390  		router.Methods(http.MethodPut).HandlerFunc(
   391  			CollectAPIStats("putbuckettagging", MaxClients(HTTPTraceAll(api.PutBucketTaggingHandler)))).Queries("tagging", "")
   392  		// PutBucketVersioning
   393  		router.Methods(http.MethodPut).HandlerFunc(
   394  			CollectAPIStats("putbucketversioning", MaxClients(HTTPTraceAll(api.PutBucketVersioningHandler)))).Queries("versioning", "")
   395  		// PutBucketNotification
   396  		router.Methods(http.MethodPut).HandlerFunc(
   397  			CollectAPIStats("putbucketnotification", MaxClients(HTTPTraceAll(api.PutBucketNotificationHandler)))).Queries("notification", "")
   398  		// PutBucket
   399  		router.Methods(http.MethodPut).HandlerFunc(
   400  			CollectAPIStats("putbucket", MaxClients(HTTPTraceAll(api.PutBucketHandler))))
   401  		// HeadBucket
   402  		router.Methods(http.MethodHead).HandlerFunc(
   403  			CollectAPIStats("headbucket", MaxClients(HTTPTraceAll(api.HeadBucketHandler))))
   404  		// PostPolicy
   405  		router.Methods(http.MethodPost).HeadersRegexp(xhttp.ContentType, "multipart/form-data*").HandlerFunc(
   406  			CollectAPIStats("postpolicybucket", MaxClients(HTTPTraceHdrs(api.PostPolicyBucketHandler))))
   407  		// DeleteMultipleObjects
   408  		router.Methods(http.MethodPost).HandlerFunc(
   409  			CollectAPIStats("deletemultipleobjects", MaxClients(HTTPTraceAll(api.DeleteMultipleObjectsHandler)))).Queries("delete", "")
   410  		// DeleteBucketPolicy
   411  		router.Methods(http.MethodDelete).HandlerFunc(
   412  			CollectAPIStats("deletebucketpolicy", MaxClients(HTTPTraceAll(api.DeleteBucketPolicyHandler)))).Queries("policy", "")
   413  		// DeleteBucketReplication
   414  		router.Methods(http.MethodDelete).HandlerFunc(
   415  			CollectAPIStats("deletebucketreplicationconfiguration", MaxClients(HTTPTraceAll(api.DeleteBucketReplicationConfigHandler)))).Queries("replication", "")
   416  		// DeleteBucketLifecycle
   417  		router.Methods(http.MethodDelete).HandlerFunc(
   418  			CollectAPIStats("deletebucketlifecycle", MaxClients(HTTPTraceAll(api.DeleteBucketLifecycleHandler)))).Queries("lifecycle", "")
   419  		// DeleteBucketEncryption
   420  		router.Methods(http.MethodDelete).HandlerFunc(
   421  			CollectAPIStats("deletebucketencryption", MaxClients(HTTPTraceAll(api.DeleteBucketEncryptionHandler)))).Queries("encryption", "")
   422  		// DeleteBucket
   423  		router.Methods(http.MethodDelete).HandlerFunc(
   424  			CollectAPIStats("deletebucket", MaxClients(HTTPTraceAll(api.DeleteBucketHandler))))
   425  		// MinIO extension API for replication.
   426  		//
   427  		// GetBucketReplicationMetrics
   428  		router.Methods(http.MethodGet).HandlerFunc(
   429  			CollectAPIStats("getbucketreplicationmetrics", MaxClients(HTTPTraceAll(api.GetBucketReplicationMetricsHandler)))).Queries("replication-metrics", "")
   430  
   431  		// S3 ListObjectsV1 (Legacy)
   432  		router.Methods(http.MethodGet).HandlerFunc(
   433  			CollectAPIStats("listobjectsv1", MaxClients(HTTPTraceAll(api.ListObjectsV1Handler))))
   434  
   435  	}
   436  
   437  	/// Root operation
   438  
   439  	// ListenNotification
   440  	apiRouter.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
   441  		CollectAPIStats("listennotification", MaxClients(HTTPTraceAll(api.ListenNotificationHandler)))).Queries("events", "{events:.*}")
   442  
   443  	// ListBuckets
   444  	apiRouter.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
   445  		CollectAPIStats("listbuckets", MaxClients(HTTPTraceAll(api.ListBucketsHandler))))
   446  
   447  	// S3 browser with signature v4 adds '//' for ListBuckets request, so rather
   448  	// than failing with UnknownAPIRequest we simply handle it for now.
   449  	apiRouter.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc(
   450  		CollectAPIStats("listbuckets", MaxClients(HTTPTraceAll(api.ListBucketsHandler))))
   451  
   452  	// If none of the routes match add default error handler routes
   453  	apiRouter.NotFoundHandler = CollectAPIStats("notfound", HTTPTraceAll(ErrorResponseHandler))
   454  	apiRouter.MethodNotAllowedHandler = CollectAPIStats("methodnotallowed", HTTPTraceAll(MethodNotAllowedHandler("S3")))
   455  
   456  }
   457  
   458  // corsHandler handler for CORS (Cross Origin Resource Sharing)
   459  func corsHandler(handler http.Handler) http.Handler {
   460  	commonS3Headers := []string{
   461  		xhttp.Date,
   462  		xhttp.ETag,
   463  		xhttp.ServerInfo,
   464  		xhttp.Connection,
   465  		xhttp.AcceptRanges,
   466  		xhttp.ContentRange,
   467  		xhttp.ContentEncoding,
   468  		xhttp.ContentLength,
   469  		xhttp.ContentType,
   470  		xhttp.ContentDisposition,
   471  		xhttp.LastModified,
   472  		xhttp.ContentLanguage,
   473  		xhttp.CacheControl,
   474  		xhttp.RetryAfter,
   475  		xhttp.AmzBucketRegion,
   476  		xhttp.Expires,
   477  		"X-Amz*",
   478  		"x-amz*",
   479  		"*",
   480  	}
   481  
   482  	return cors.New(cors.Options{
   483  		AllowOriginFunc: func(origin string) bool {
   484  			for _, allowedOrigin := range globalAPIConfig.getCorsAllowOrigins() {
   485  				if wildcard.MatchSimple(allowedOrigin, origin) {
   486  					return true
   487  				}
   488  			}
   489  			return false
   490  		},
   491  		AllowedMethods: []string{
   492  			http.MethodGet,
   493  			http.MethodPut,
   494  			http.MethodHead,
   495  			http.MethodPost,
   496  			http.MethodDelete,
   497  			http.MethodOptions,
   498  			http.MethodPatch,
   499  		},
   500  		AllowedHeaders:   commonS3Headers,
   501  		ExposedHeaders:   commonS3Headers,
   502  		AllowCredentials: true,
   503  	}).Handler(handler)
   504  }