mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Virtual host style S3 requests (#5095)
This commit is contained in:
parent
d57d57ddf5
commit
e7a724de0d
@ -985,7 +985,7 @@ func (adminAPI adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var config serverConfigV19
|
var config serverConfigV20
|
||||||
err = json.Unmarshal(configBytes, &config)
|
err = json.Unmarshal(configBytes, &config)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -162,6 +162,7 @@ const (
|
|||||||
ErrServerNotInitialized
|
ErrServerNotInitialized
|
||||||
ErrOperationTimedOut
|
ErrOperationTimedOut
|
||||||
ErrPartsSizeUnequal
|
ErrPartsSizeUnequal
|
||||||
|
ErrInvalidRequest
|
||||||
// Add new extended error codes here.
|
// Add new extended error codes here.
|
||||||
// Please open a https://github.com/minio/minio/issues before adding
|
// Please open a https://github.com/minio/minio/issues before adding
|
||||||
// new error codes here.
|
// new error codes here.
|
||||||
@ -731,6 +732,15 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
|||||||
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",
|
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,
|
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. errorIf()
|
||||||
|
// 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,
|
||||||
|
},
|
||||||
|
|
||||||
// Add your error structure here.
|
// Add your error structure here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,9 +16,8 @@
|
|||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import router "github.com/gorilla/mux"
|
||||||
router "github.com/gorilla/mux"
|
import "net/http"
|
||||||
)
|
|
||||||
|
|
||||||
// objectAPIHandler implements and provides http handlers for S3 API.
|
// objectAPIHandler implements and provides http handlers for S3 API.
|
||||||
type objectAPIHandlers struct {
|
type objectAPIHandlers struct {
|
||||||
@ -34,70 +33,75 @@ func registerAPIRouter(mux *router.Router) {
|
|||||||
|
|
||||||
// API Router
|
// API Router
|
||||||
apiRouter := mux.NewRoute().PathPrefix("/").Subrouter()
|
apiRouter := mux.NewRoute().PathPrefix("/").Subrouter()
|
||||||
|
var routers []*router.Router
|
||||||
|
if globalDomainName != "" {
|
||||||
|
routers = append(routers, apiRouter.Host("{bucket:.+}."+globalDomainName).Subrouter())
|
||||||
|
}
|
||||||
|
routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
|
||||||
|
|
||||||
// Bucket router
|
for _, bucket := range routers {
|
||||||
bucket := apiRouter.PathPrefix("/{bucket}").Subrouter()
|
// Object operations
|
||||||
|
// HeadObject
|
||||||
|
bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler))
|
||||||
|
// CopyObjectPart
|
||||||
|
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||||
|
// PutObjectPart
|
||||||
|
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||||
|
// ListObjectPxarts
|
||||||
|
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
|
// CompleteMultipartUpload
|
||||||
|
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
|
// NewMultipartUpload
|
||||||
|
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "")
|
||||||
|
// AbortMultipartUpload
|
||||||
|
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
|
// GetObject
|
||||||
|
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler))
|
||||||
|
// CopyObject
|
||||||
|
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler))
|
||||||
|
// PutObject
|
||||||
|
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler))
|
||||||
|
// DeleteObject
|
||||||
|
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler))
|
||||||
|
|
||||||
/// Object operations
|
/// Bucket operations
|
||||||
|
// GetBucketLocation
|
||||||
// HeadObject
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "")
|
||||||
bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler))
|
// GetBucketPolicy
|
||||||
// CopyObjectPart
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "")
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
// GetBucketNotification
|
||||||
// PutObjectPart
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "")
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
// ListenBucketNotification
|
||||||
// ListObjectPxarts
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}")
|
||||||
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}")
|
// ListMultipartUploads
|
||||||
// CompleteMultipartUpload
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "")
|
||||||
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
// ListObjectsV2
|
||||||
// NewMultipartUpload
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2")
|
||||||
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "")
|
// ListObjectsV1 (Legacy)
|
||||||
// AbortMultipartUpload
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV1Handler))
|
||||||
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
// PutBucketPolicy
|
||||||
// GetObject
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "")
|
||||||
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler))
|
// PutBucketNotification
|
||||||
// CopyObject
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "")
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler))
|
// PutBucket
|
||||||
// PutObject
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketHandler))
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler))
|
// HeadBucket
|
||||||
// DeleteObject
|
bucket.Methods("HEAD").HandlerFunc(httpTraceAll(api.HeadBucketHandler))
|
||||||
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler))
|
// PostPolicy
|
||||||
|
bucket.Methods("POST").Path("/").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(httpTraceAll(api.PostPolicyBucketHandler))
|
||||||
/// Bucket operations
|
// DeleteMultipleObjects
|
||||||
|
bucket.Methods("POST").HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler)).Queries("delete", "")
|
||||||
// GetBucketLocation
|
// DeleteBucketPolicy
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "")
|
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "")
|
||||||
// GetBucketPolicy
|
// DeleteBucket
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "")
|
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketHandler))
|
||||||
// GetBucketNotification
|
}
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "")
|
|
||||||
// ListenBucketNotification
|
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}")
|
|
||||||
// ListMultipartUploads
|
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "")
|
|
||||||
// ListObjectsV2
|
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2")
|
|
||||||
// ListObjectsV1 (Legacy)
|
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV1Handler))
|
|
||||||
// PutBucketPolicy
|
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "")
|
|
||||||
// PutBucketNotification
|
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "")
|
|
||||||
// PutBucket
|
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketHandler))
|
|
||||||
// HeadBucket
|
|
||||||
bucket.Methods("HEAD").HandlerFunc(httpTraceAll(api.HeadBucketHandler))
|
|
||||||
// PostPolicy
|
|
||||||
bucket.Methods("POST").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(httpTraceAll(api.PostPolicyBucketHandler))
|
|
||||||
// DeleteMultipleObjects
|
|
||||||
bucket.Methods("POST").HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler))
|
|
||||||
// DeleteBucketPolicy
|
|
||||||
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "")
|
|
||||||
// DeleteBucket
|
|
||||||
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketHandler))
|
|
||||||
|
|
||||||
/// Root operation
|
/// Root operation
|
||||||
|
|
||||||
// ListBuckets
|
// ListBuckets
|
||||||
apiRouter.Methods("GET").HandlerFunc(httpTraceAll(api.ListBucketsHandler))
|
apiRouter.Methods("GET").Path("/").HandlerFunc(httpTraceAll(api.ListBucketsHandler))
|
||||||
|
|
||||||
|
// If none of the routes match.
|
||||||
|
apiRouter.NotFoundHandler = http.HandlerFunc(httpTraceAll(notFoundHandler))
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,11 @@ func checkRequestAuthType(r *http.Request, bucket, policyAction, region string)
|
|||||||
if reqAuthType == authTypeAnonymous && policyAction != "" {
|
if reqAuthType == authTypeAnonymous && policyAction != "" {
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
||||||
sourceIP := getSourceIPAddress(r)
|
sourceIP := getSourceIPAddress(r)
|
||||||
return enforceBucketPolicy(bucket, policyAction, r.URL.Path,
|
resource, err := getResource(r.URL.Path, r.Host, globalDomainName)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInternalError
|
||||||
|
}
|
||||||
|
return enforceBucketPolicy(bucket, policyAction, resource,
|
||||||
r.Referer(), sourceIP, r.URL.Query())
|
r.Referer(), sourceIP, r.URL.Query())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -441,10 +440,6 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
|
|
||||||
// Make sure that the URL does not contain object name.
|
// Make sure that the URL does not contain object name.
|
||||||
bucket := mux.Vars(r)["bucket"]
|
bucket := mux.Vars(r)["bucket"]
|
||||||
if bucket != filepath.Clean(r.URL.Path[1:]) {
|
|
||||||
writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Require Content-Length to be set in the request
|
// Require Content-Length to be set in the request
|
||||||
size := r.ContentLength
|
size := r.ContentLength
|
||||||
|
@ -57,7 +57,7 @@ func initConfig() {
|
|||||||
// Config file does not exist, we create it fresh and return upon success.
|
// Config file does not exist, we create it fresh and return upon success.
|
||||||
if isFile(getConfigFile()) {
|
if isFile(getConfigFile()) {
|
||||||
fatalIf(migrateConfig(), "Config migration failed.")
|
fatalIf(migrateConfig(), "Config migration failed.")
|
||||||
fatalIf(loadConfig(), "Unable to load config version: '%s'.", v19)
|
fatalIf(loadConfig(), "Unable to load config version: '%s'.", v20)
|
||||||
} else {
|
} else {
|
||||||
fatalIf(newConfig(), "Unable to initialize minio config for the first time.")
|
fatalIf(newConfig(), "Unable to initialize minio config for the first time.")
|
||||||
log.Println("Created minio configuration file successfully at " + getConfigDir())
|
log.Println("Created minio configuration file successfully at " + getConfigDir())
|
||||||
@ -117,4 +117,9 @@ func handleCommonEnvVars() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalHTTPTrace = os.Getenv("MINIO_HTTP_TRACE") != ""
|
globalHTTPTrace = os.Getenv("MINIO_HTTP_TRACE") != ""
|
||||||
|
|
||||||
|
globalDomainName = os.Getenv("MINIO_DOMAIN")
|
||||||
|
if globalDomainName != "" {
|
||||||
|
globalIsEnvDomainName = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,12 @@ func migrateConfig() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case v19:
|
case "19":
|
||||||
|
if err = migrateV19ToV20(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case "20":
|
||||||
// No migration needed. this always points to current version.
|
// No migration needed. this always points to current version.
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@ -1479,3 +1484,111 @@ func migrateV18ToV19() error {
|
|||||||
log.Printf(configMigrateMSGTemplate, configFile, cv18.Version, srvConfig.Version)
|
log.Printf(configMigrateMSGTemplate, configFile, cv18.Version, srvConfig.Version)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func migrateV19ToV20() error {
|
||||||
|
configFile := getConfigFile()
|
||||||
|
|
||||||
|
cv19 := &serverConfigV19{}
|
||||||
|
_, err := quick.Load(configFile, cv19)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load config version ‘18’. %v", err)
|
||||||
|
}
|
||||||
|
if cv19.Version != "19" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over fields from V19 into V20 config struct
|
||||||
|
srvConfig := &serverConfigV20{
|
||||||
|
Logger: &loggers{},
|
||||||
|
Notify: ¬ifier{},
|
||||||
|
}
|
||||||
|
srvConfig.Version = "20"
|
||||||
|
srvConfig.Credential = cv19.Credential
|
||||||
|
srvConfig.Region = cv19.Region
|
||||||
|
if srvConfig.Region == "" {
|
||||||
|
// Region needs to be set for AWS Signature Version 4.
|
||||||
|
srvConfig.Region = globalMinioDefaultRegion
|
||||||
|
}
|
||||||
|
|
||||||
|
srvConfig.Logger.Console = cv19.Logger.Console
|
||||||
|
srvConfig.Logger.File = cv19.Logger.File
|
||||||
|
|
||||||
|
// check and set notifiers config
|
||||||
|
if len(cv19.Notify.AMQP) == 0 {
|
||||||
|
srvConfig.Notify.AMQP = make(map[string]amqpNotify)
|
||||||
|
srvConfig.Notify.AMQP["1"] = amqpNotify{}
|
||||||
|
} else {
|
||||||
|
// New deliveryMode parameter is added for AMQP,
|
||||||
|
// default value is already 0, so nothing to
|
||||||
|
// explicitly migrate here.
|
||||||
|
srvConfig.Notify.AMQP = cv19.Notify.AMQP
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.ElasticSearch) == 0 {
|
||||||
|
srvConfig.Notify.ElasticSearch = make(map[string]elasticSearchNotify)
|
||||||
|
srvConfig.Notify.ElasticSearch["1"] = elasticSearchNotify{
|
||||||
|
Format: formatNamespace,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.ElasticSearch = cv19.Notify.ElasticSearch
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.Redis) == 0 {
|
||||||
|
srvConfig.Notify.Redis = make(map[string]redisNotify)
|
||||||
|
srvConfig.Notify.Redis["1"] = redisNotify{
|
||||||
|
Format: formatNamespace,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.Redis = cv19.Notify.Redis
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.PostgreSQL) == 0 {
|
||||||
|
srvConfig.Notify.PostgreSQL = make(map[string]postgreSQLNotify)
|
||||||
|
srvConfig.Notify.PostgreSQL["1"] = postgreSQLNotify{
|
||||||
|
Format: formatNamespace,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.PostgreSQL = cv19.Notify.PostgreSQL
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.Kafka) == 0 {
|
||||||
|
srvConfig.Notify.Kafka = make(map[string]kafkaNotify)
|
||||||
|
srvConfig.Notify.Kafka["1"] = kafkaNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.Kafka = cv19.Notify.Kafka
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.NATS) == 0 {
|
||||||
|
srvConfig.Notify.NATS = make(map[string]natsNotify)
|
||||||
|
srvConfig.Notify.NATS["1"] = natsNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.NATS = cv19.Notify.NATS
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.Webhook) == 0 {
|
||||||
|
srvConfig.Notify.Webhook = make(map[string]webhookNotify)
|
||||||
|
srvConfig.Notify.Webhook["1"] = webhookNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.Webhook = cv19.Notify.Webhook
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.MySQL) == 0 {
|
||||||
|
srvConfig.Notify.MySQL = make(map[string]mySQLNotify)
|
||||||
|
srvConfig.Notify.MySQL["1"] = mySQLNotify{
|
||||||
|
Format: formatNamespace,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.MySQL = cv19.Notify.MySQL
|
||||||
|
}
|
||||||
|
if len(cv19.Notify.MQTT) == 0 {
|
||||||
|
srvConfig.Notify.MQTT = make(map[string]mqttNotify)
|
||||||
|
srvConfig.Notify.MQTT["1"] = mqttNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.MQTT = cv19.Notify.MQTT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load browser config from existing config in the file.
|
||||||
|
srvConfig.Browser = cv19.Browser
|
||||||
|
|
||||||
|
if err = quick.Save(configFile, srvConfig); err != nil {
|
||||||
|
return fmt.Errorf("Failed to migrate config from ‘%s’ to ‘%s’. %v", cv19.Version, srvConfig.Version, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(configMigrateMSGTemplate, configFile, cv19.Version, srvConfig.Version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -169,7 +169,7 @@ func TestServerConfigMigrateV2toV19(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the version number in the upgraded config file
|
// Check the version number in the upgraded config file
|
||||||
expectedVersion := v19
|
expectedVersion := v20
|
||||||
if serverConfig.Version != expectedVersion {
|
if serverConfig.Version != expectedVersion {
|
||||||
t.Fatalf("Expect version "+expectedVersion+", found: %v", serverConfig.Version)
|
t.Fatalf("Expect version "+expectedVersion+", found: %v", serverConfig.Version)
|
||||||
}
|
}
|
||||||
|
@ -472,3 +472,21 @@ type serverConfigV18 struct {
|
|||||||
// Notification queue configuration.
|
// Notification queue configuration.
|
||||||
Notify *notifier `json:"notify"`
|
Notify *notifier `json:"notify"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serverConfigV19 server configuration version '19' which is like
|
||||||
|
// version '18' except it adds support for MQTT notifications.
|
||||||
|
type serverConfigV19 struct {
|
||||||
|
sync.RWMutex
|
||||||
|
Version string `json:"version"`
|
||||||
|
|
||||||
|
// S3 API configuration.
|
||||||
|
Credential auth.Credentials `json:"credential"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Browser BrowserFlag `json:"browser"`
|
||||||
|
|
||||||
|
// Additional error logging configuration.
|
||||||
|
Logger *loggers `json:"logger"`
|
||||||
|
|
||||||
|
// Notification queue configuration.
|
||||||
|
Notify *notifier `json:"notify"`
|
||||||
|
}
|
||||||
|
@ -28,17 +28,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Config version
|
// Config version
|
||||||
const v19 = "19"
|
const v20 = "20"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// serverConfig server config.
|
// serverConfig server config.
|
||||||
serverConfig *serverConfigV19
|
serverConfig *serverConfigV20
|
||||||
serverConfigMu sync.RWMutex
|
serverConfigMu sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverConfigV19 server configuration version '19' which is like
|
// serverConfigV20 server configuration version '20' which is like
|
||||||
// version '18' except it adds support for MQTT notifications.
|
// version '19' except it adds support for VirtualHostDomain
|
||||||
type serverConfigV19 struct {
|
type serverConfigV20 struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
|
||||||
@ -46,6 +46,7 @@ type serverConfigV19 struct {
|
|||||||
Credential auth.Credentials `json:"credential"`
|
Credential auth.Credentials `json:"credential"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
Browser BrowserFlag `json:"browser"`
|
Browser BrowserFlag `json:"browser"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
|
||||||
// Additional error logging configuration.
|
// Additional error logging configuration.
|
||||||
Logger *loggers `json:"logger"`
|
Logger *loggers `json:"logger"`
|
||||||
@ -55,7 +56,7 @@ type serverConfigV19 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetVersion get current config version.
|
// GetVersion get current config version.
|
||||||
func (s *serverConfigV19) GetVersion() string {
|
func (s *serverConfigV20) GetVersion() string {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ func (s *serverConfigV19) GetVersion() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetRegion set a new region.
|
// SetRegion set a new region.
|
||||||
func (s *serverConfigV19) SetRegion(region string) {
|
func (s *serverConfigV20) SetRegion(region string) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ func (s *serverConfigV19) SetRegion(region string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRegion get current region.
|
// GetRegion get current region.
|
||||||
func (s *serverConfigV19) GetRegion() string {
|
func (s *serverConfigV20) GetRegion() string {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ func (s *serverConfigV19) GetRegion() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetCredentials set new credentials. SetCredential returns the previous credential.
|
// SetCredentials set new credentials. SetCredential returns the previous credential.
|
||||||
func (s *serverConfigV19) SetCredential(creds auth.Credentials) (prevCred auth.Credentials) {
|
func (s *serverConfigV20) SetCredential(creds auth.Credentials) (prevCred auth.Credentials) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ func (s *serverConfigV19) SetCredential(creds auth.Credentials) (prevCred auth.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCredentials get current credentials.
|
// GetCredentials get current credentials.
|
||||||
func (s *serverConfigV19) GetCredential() auth.Credentials {
|
func (s *serverConfigV20) GetCredential() auth.Credentials {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ func (s *serverConfigV19) GetCredential() auth.Credentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBrowser set if browser is enabled.
|
// SetBrowser set if browser is enabled.
|
||||||
func (s *serverConfigV19) SetBrowser(b bool) {
|
func (s *serverConfigV20) SetBrowser(b bool) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ func (s *serverConfigV19) SetBrowser(b bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCredentials get current credentials.
|
// GetCredentials get current credentials.
|
||||||
func (s *serverConfigV19) GetBrowser() bool {
|
func (s *serverConfigV20) GetBrowser() bool {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ func (s *serverConfigV19) GetBrowser() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save config.
|
// Save config.
|
||||||
func (s *serverConfigV19) Save() error {
|
func (s *serverConfigV20) Save() error {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
@ -128,9 +129,9 @@ func (s *serverConfigV19) Save() error {
|
|||||||
return quick.Save(getConfigFile(), s)
|
return quick.Save(getConfigFile(), s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServerConfigV19() *serverConfigV19 {
|
func newServerConfigV20() *serverConfigV20 {
|
||||||
srvCfg := &serverConfigV19{
|
srvCfg := &serverConfigV20{
|
||||||
Version: v19,
|
Version: v20,
|
||||||
Credential: auth.MustGetNewCredentials(),
|
Credential: auth.MustGetNewCredentials(),
|
||||||
Region: globalMinioDefaultRegion,
|
Region: globalMinioDefaultRegion,
|
||||||
Browser: true,
|
Browser: true,
|
||||||
@ -168,7 +169,7 @@ func newServerConfigV19() *serverConfigV19 {
|
|||||||
// found, otherwise use default parameters
|
// found, otherwise use default parameters
|
||||||
func newConfig() error {
|
func newConfig() error {
|
||||||
// Initialize server config.
|
// Initialize server config.
|
||||||
srvCfg := newServerConfigV19()
|
srvCfg := newServerConfigV20()
|
||||||
|
|
||||||
// If env is set override the credentials from config file.
|
// If env is set override the credentials from config file.
|
||||||
if globalIsEnvCreds {
|
if globalIsEnvCreds {
|
||||||
@ -183,6 +184,10 @@ func newConfig() error {
|
|||||||
srvCfg.SetRegion(globalServerRegion)
|
srvCfg.SetRegion(globalServerRegion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globalIsEnvDomainName {
|
||||||
|
srvCfg.Domain = globalDomainName
|
||||||
|
}
|
||||||
|
|
||||||
// hold the mutex lock before a new config is assigned.
|
// hold the mutex lock before a new config is assigned.
|
||||||
// Save the new config globally.
|
// Save the new config globally.
|
||||||
// unlock the mutex.
|
// unlock the mutex.
|
||||||
@ -246,8 +251,8 @@ func checkDupJSONKeys(json string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getValidConfig - returns valid server configuration
|
// getValidConfig - returns valid server configuration
|
||||||
func getValidConfig() (*serverConfigV19, error) {
|
func getValidConfig() (*serverConfigV20, error) {
|
||||||
srvCfg := &serverConfigV19{
|
srvCfg := &serverConfigV20{
|
||||||
Region: globalMinioDefaultRegion,
|
Region: globalMinioDefaultRegion,
|
||||||
Browser: true,
|
Browser: true,
|
||||||
}
|
}
|
||||||
@ -257,8 +262,8 @@ func getValidConfig() (*serverConfigV19, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if srvCfg.Version != v19 {
|
if srvCfg.Version != v20 {
|
||||||
return nil, fmt.Errorf("configuration version mismatch. Expected: ‘%s’, Got: ‘%s’", v19, srvCfg.Version)
|
return nil, fmt.Errorf("configuration version mismatch. Expected: ‘%s’, Got: ‘%s’", v20, srvCfg.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config file json and check for duplication json keys
|
// Load config file json and check for duplication json keys
|
||||||
@ -312,6 +317,10 @@ func loadConfig() error {
|
|||||||
srvCfg.SetRegion(globalServerRegion)
|
srvCfg.SetRegion(globalServerRegion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globalIsEnvDomainName {
|
||||||
|
srvCfg.Domain = globalDomainName
|
||||||
|
}
|
||||||
|
|
||||||
// hold the mutex lock before a new config is assigned.
|
// hold the mutex lock before a new config is assigned.
|
||||||
serverConfigMu.Lock()
|
serverConfigMu.Lock()
|
||||||
serverConfig = srvCfg
|
serverConfig = srvCfg
|
||||||
@ -324,6 +333,9 @@ func loadConfig() error {
|
|||||||
if !globalIsEnvRegion {
|
if !globalIsEnvRegion {
|
||||||
globalServerRegion = serverConfig.GetRegion()
|
globalServerRegion = serverConfig.GetRegion()
|
||||||
}
|
}
|
||||||
|
if !globalIsEnvDomainName {
|
||||||
|
globalDomainName = serverConfig.Domain
|
||||||
|
}
|
||||||
serverConfigMu.Unlock()
|
serverConfigMu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
@ -117,8 +117,8 @@ func TestServerConfig(t *testing.T) {
|
|||||||
serverConfig.Logger.SetFile(fileLogger)
|
serverConfig.Logger.SetFile(fileLogger)
|
||||||
|
|
||||||
// Match version.
|
// Match version.
|
||||||
if serverConfig.GetVersion() != v19 {
|
if serverConfig.GetVersion() != v20 {
|
||||||
t.Errorf("Expecting version %s found %s", serverConfig.GetVersion(), v19)
|
t.Errorf("Expecting version %s found %s", serverConfig.GetVersion(), v20)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to save.
|
// Attempt to save.
|
||||||
@ -149,6 +149,9 @@ func TestServerConfigWithEnvs(t *testing.T) {
|
|||||||
os.Setenv("MINIO_REGION", "us-west-1")
|
os.Setenv("MINIO_REGION", "us-west-1")
|
||||||
defer os.Unsetenv("MINIO_REGION")
|
defer os.Unsetenv("MINIO_REGION")
|
||||||
|
|
||||||
|
os.Setenv("MINIO_DOMAIN", "domain.com")
|
||||||
|
defer os.Unsetenv("MINIO_DOMAIN")
|
||||||
|
|
||||||
defer resetGlobalIsEnvs()
|
defer resetGlobalIsEnvs()
|
||||||
|
|
||||||
// Get test root.
|
// Get test root.
|
||||||
@ -189,6 +192,9 @@ func TestServerConfigWithEnvs(t *testing.T) {
|
|||||||
t.Errorf("Expecting access key to be `minio123` found %s", cred.SecretKey)
|
t.Errorf("Expecting access key to be `minio123` found %s", cred.SecretKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if serverConfig.Domain != "domain.com" {
|
||||||
|
t.Errorf("Expecting Domain to be `domain.com` found " + serverConfig.Domain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDupJSONKeys(t *testing.T) {
|
func TestCheckDupJSONKeys(t *testing.T) {
|
||||||
@ -231,7 +237,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
|
|
||||||
configPath := filepath.Join(rootPath, minioConfigFile)
|
configPath := filepath.Join(rootPath, minioConfigFile)
|
||||||
|
|
||||||
v := v19
|
v := v20
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
configData string
|
configData string
|
@ -18,6 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
router "github.com/gorilla/mux"
|
router "github.com/gorilla/mux"
|
||||||
"github.com/minio/minio-go/pkg/policy"
|
"github.com/minio/minio-go/pkg/policy"
|
||||||
@ -62,69 +63,78 @@ func registerGatewayAPIRouter(mux *router.Router, gw GatewayLayer) {
|
|||||||
// API Router
|
// API Router
|
||||||
apiRouter := mux.NewRoute().PathPrefix("/").Subrouter()
|
apiRouter := mux.NewRoute().PathPrefix("/").Subrouter()
|
||||||
|
|
||||||
// Bucket router
|
var routers []*router.Router
|
||||||
bucket := apiRouter.PathPrefix("/{bucket}").Subrouter()
|
if globalDomainName != "" {
|
||||||
|
routers = append(routers, apiRouter.Host("{bucket:.+}."+globalDomainName).Subrouter())
|
||||||
|
}
|
||||||
|
routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
|
||||||
|
|
||||||
/// Object operations
|
// Object operations
|
||||||
|
for _, bucket := range routers {
|
||||||
|
/// Object operations
|
||||||
|
|
||||||
// HeadObject
|
// HeadObject
|
||||||
bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler))
|
bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler))
|
||||||
// CopyObjectPart
|
// CopyObjectPart
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||||
// PutObjectPart
|
// PutObjectPart
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||||
// ListObjectPxarts
|
// ListObjectPxarts
|
||||||
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}")
|
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
// CompleteMultipartUpload
|
// CompleteMultipartUpload
|
||||||
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
// NewMultipartUpload
|
// NewMultipartUpload
|
||||||
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "")
|
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "")
|
||||||
// AbortMultipartUpload
|
// AbortMultipartUpload
|
||||||
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}")
|
||||||
// GetObject
|
// GetObject
|
||||||
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler))
|
bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler))
|
||||||
// CopyObject
|
// CopyObject
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler))
|
bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler))
|
||||||
// PutObject
|
// PutObject
|
||||||
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler))
|
bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler))
|
||||||
// DeleteObject
|
// DeleteObject
|
||||||
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler))
|
bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler))
|
||||||
|
|
||||||
/// Bucket operations
|
/// Bucket operations
|
||||||
|
|
||||||
// GetBucketLocation
|
// GetBucketLocation
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "")
|
||||||
// GetBucketPolicy
|
// GetBucketPolicy
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "")
|
||||||
// GetBucketNotification
|
// GetBucketNotification
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "")
|
||||||
// ListenBucketNotification
|
// ListenBucketNotification
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}")
|
||||||
// ListMultipartUploads
|
// ListMultipartUploads
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "")
|
||||||
// ListObjectsV2
|
// ListObjectsV2
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2")
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2")
|
||||||
// ListObjectsV1 (Legacy)
|
// ListObjectsV1 (Legacy)
|
||||||
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV1Handler))
|
bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV1Handler))
|
||||||
// PutBucketPolicy
|
// PutBucketPolicy
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "")
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "")
|
||||||
// PutBucketNotification
|
// PutBucketNotification
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "")
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "")
|
||||||
// PutBucket
|
// PutBucket
|
||||||
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketHandler))
|
bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketHandler))
|
||||||
// HeadBucket
|
// HeadBucket
|
||||||
bucket.Methods("HEAD").HandlerFunc(httpTraceAll(api.HeadBucketHandler))
|
bucket.Methods("HEAD").HandlerFunc(httpTraceAll(api.HeadBucketHandler))
|
||||||
// PostPolicy
|
// PostPolicy
|
||||||
bucket.Methods("POST").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(httpTraceAll(api.PostPolicyBucketHandler))
|
bucket.Methods("POST").Path("/").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(httpTraceAll(api.PostPolicyBucketHandler))
|
||||||
// DeleteMultipleObjects
|
// DeleteMultipleObjects
|
||||||
bucket.Methods("POST").HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler))
|
bucket.Methods("POST").HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler)).Queries("delete", "")
|
||||||
// DeleteBucketPolicy
|
// DeleteBucketPolicy
|
||||||
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "")
|
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "")
|
||||||
// DeleteBucket
|
// DeleteBucket
|
||||||
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketHandler))
|
bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketHandler))
|
||||||
|
}
|
||||||
|
|
||||||
/// Root operation
|
/// Root operation
|
||||||
|
|
||||||
// ListBuckets
|
// ListBuckets
|
||||||
apiRouter.Methods("GET").HandlerFunc(httpTraceAll(api.ListBucketsHandler))
|
apiRouter.Methods("GET").Path("/").HandlerFunc(httpTraceAll(api.ListBucketsHandler))
|
||||||
|
|
||||||
|
// If none of the routes match.
|
||||||
|
apiRouter.NotFoundHandler = http.HandlerFunc(httpTraceAll(notFoundHandler))
|
||||||
}
|
}
|
||||||
|
@ -440,11 +440,6 @@ func (h resourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A put method on path "/" doesn't make sense, ignore it.
|
|
||||||
if r.Method == httpPUT && r.URL.Path == "/" && r.Header.Get(minioAdminOpHeader) == "" {
|
|
||||||
writeErrorResponse(w, ErrNotImplemented, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve HTTP.
|
// Serve HTTP.
|
||||||
h.handler.ServeHTTP(w, r)
|
h.handler.ServeHTTP(w, r)
|
||||||
|
@ -146,6 +146,9 @@ var (
|
|||||||
globalActiveCred auth.Credentials
|
globalActiveCred auth.Credentials
|
||||||
globalPublicCerts []*x509.Certificate
|
globalPublicCerts []*x509.Certificate
|
||||||
globalXLObjCacheDisabled bool
|
globalXLObjCacheDisabled bool
|
||||||
|
|
||||||
|
globalIsEnvDomainName bool
|
||||||
|
globalDomainName string // Root domain for virtual host style requests
|
||||||
// Add new variable global values here.
|
// Add new variable global values here.
|
||||||
|
|
||||||
globalListingTimeout = newDynamicTimeout( /*30*/ 600*time.Second /*5*/, 600*time.Second) // timeout for listing related ops
|
globalListingTimeout = newDynamicTimeout( /*30*/ 600*time.Second /*5*/, 600*time.Second) // timeout for listing related ops
|
||||||
|
@ -19,6 +19,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -259,3 +260,30 @@ func httpTraceHdrs(f http.HandlerFunc) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
return httptracer.TraceReqHandlerFunc(f, os.Stdout, false)
|
return httptracer.TraceReqHandlerFunc(f, os.Stdout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns "/bucketName/objectName" for path-style or virtual-host-style requests.
|
||||||
|
func getResource(path string, host string, domain string) (string, error) {
|
||||||
|
if domain == "" {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
// If virtual-host-style is enabled construct the "resource" properly.
|
||||||
|
if strings.Contains(host, ":") {
|
||||||
|
// In bucket.mydomain.com:9000, strip out :9000
|
||||||
|
var err error
|
||||||
|
if host, _, err = net.SplitHostPort(host); err != nil {
|
||||||
|
errorIf(err, "Unable to split %s", host)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(host, "."+domain) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
bucket := strings.TrimSuffix(host, "."+domain)
|
||||||
|
return slashSeparator + pathJoin(bucket, path), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the http routes match respond with MethodNotAllowed
|
||||||
|
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -190,3 +190,26 @@ func TestExtractMetadataHeaders(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test getResource()
|
||||||
|
func TestGetResource(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
p string
|
||||||
|
host string
|
||||||
|
domain string
|
||||||
|
expectedResource string
|
||||||
|
}{
|
||||||
|
{"/a/b/c", "test.mydomain.com", "mydomain.com", "/test/a/b/c"},
|
||||||
|
{"/a/b/c", "test.mydomain.com", "notmydomain.com", "/a/b/c"},
|
||||||
|
{"/a/b/c", "test.mydomain.com", "", "/a/b/c"},
|
||||||
|
}
|
||||||
|
for i, test := range testCases {
|
||||||
|
gotResource, err := getResource(test.p, test.host, test.domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if gotResource != test.expectedResource {
|
||||||
|
t.Fatalf("test %d: expected %s got %s", i+1, test.expectedResource, gotResource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -157,6 +157,11 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
|
|||||||
return ErrExpiredPresignRequest
|
return ErrExpiredPresignRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encodedResource, err = getResource(encodedResource, r.Host, globalDomainName)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
|
expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
|
||||||
if gotSignature != expectedSignature {
|
if gotSignature != expectedSignature {
|
||||||
return ErrSignatureDoesNotMatch
|
return ErrSignatureDoesNotMatch
|
||||||
@ -237,6 +242,11 @@ func doesSignV2Match(r *http.Request) APIErrorCode {
|
|||||||
return ErrInvalidQueryParams
|
return ErrInvalidQueryParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encodedResource, err = getResource(encodedResource, r.Host, globalDomainName)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
expectedAuth := signatureV2(r.Method, encodedResource, strings.Join(unescapedQueries, "&"), r.Header)
|
expectedAuth := signatureV2(r.Method, encodedResource, strings.Join(unescapedQueries, "&"), r.Header)
|
||||||
if v2Auth != expectedAuth {
|
if v2Auth != expectedAuth {
|
||||||
return ErrSignatureDoesNotMatch
|
return ErrSignatureDoesNotMatch
|
||||||
|
@ -69,6 +69,20 @@ export MINIO_BROWSER=off
|
|||||||
minio server /data
|
minio server /data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Domain
|
||||||
|
|Field|Type|Description|
|
||||||
|
|:---|:---|:---|
|
||||||
|
|``domain``| _string_ | Enable virtual-host-style requests i.e http://bucket.mydomain.com/object|
|
||||||
|
|
||||||
|
By default, Minio supports path-style requests which look like http://mydomain.com/bucket/object. MINIO_DOMAIN environmental varialble (or `domain` in config.json) can be used to enable virtual-host-style requests. If the request `Host` header matches with `(.+).mydomain.com` then the mattched pattern `$1` is used as bucket and the path is used as object. More information on path-style and virtual-host-style [here](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAPI.html)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export MINIO_DOMAIN=mydomain.com
|
||||||
|
minio server /data
|
||||||
|
```
|
||||||
|
|
||||||
#### Logger
|
#### Logger
|
||||||
|Field|Type|Description|
|
|Field|Type|Description|
|
||||||
|:---|:---|:---|
|
|:---|:---|:---|
|
||||||
|
Loading…
Reference in New Issue
Block a user