diff --git a/api-router.go b/api-router.go new file mode 100644 index 000000000..9a2aae15f --- /dev/null +++ b/api-router.go @@ -0,0 +1,90 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + router "github.com/gorilla/mux" + "github.com/minio/minio/pkg/fs" +) + +// storageAPI container for S3 compatible API. +type storageAPI struct { + // Filesystem instance. + Filesystem fs.Filesystem +} + +// registerAPIRouter - registers S3 compatible APIs. +func registerAPIRouter(mux *router.Router, api storageAPI) { + // API Router + apiRouter := mux.NewRoute().PathPrefix("/").Subrouter() + + // Bucket router + bucket := apiRouter.PathPrefix("/{bucket}").Subrouter() + + /// Object operations + + // HeadObject + bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(api.HeadObjectHandler) + // PutObjectPart + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(api.PutObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") + // ListObjectPxarts + bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(api.ListObjectPartsHandler).Queries("uploadId", "{uploadId:.*}") + // CompleteMultipartUpload + bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(api.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") + // NewMultipartUpload + bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(api.NewMultipartUploadHandler).Queries("uploads", "") + // AbortMultipartUpload + bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(api.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") + // GetObject + bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(api.GetObjectHandler) + // CopyObject + bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/).*?").HandlerFunc(api.CopyObjectHandler) + // PutObject + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(api.PutObjectHandler) + // DeleteObject + bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(api.DeleteObjectHandler) + + /// Bucket operations + + // GetBucketLocation + bucket.Methods("GET").HandlerFunc(api.GetBucketLocationHandler).Queries("location", "") + // GetBucketPolicy + bucket.Methods("GET").HandlerFunc(api.GetBucketPolicyHandler).Queries("policy", "") + // ListMultipartUploads + bucket.Methods("GET").HandlerFunc(api.ListMultipartUploadsHandler).Queries("uploads", "") + // ListObjects + bucket.Methods("GET").HandlerFunc(api.ListObjectsHandler) + // PutBucketPolicy + bucket.Methods("PUT").HandlerFunc(api.PutBucketPolicyHandler).Queries("policy", "") + // PutBucket + bucket.Methods("PUT").HandlerFunc(api.PutBucketHandler) + // HeadBucket + bucket.Methods("HEAD").HandlerFunc(api.HeadBucketHandler) + // PostPolicy + bucket.Methods("POST").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(api.PostPolicyBucketHandler) + // DeleteMultipleObjects + bucket.Methods("POST").HandlerFunc(api.DeleteMultipleObjectsHandler) + // DeleteBucketPolicy + bucket.Methods("DELETE").HandlerFunc(api.DeleteBucketPolicyHandler).Queries("policy", "") + // DeleteBucket + bucket.Methods("DELETE").HandlerFunc(api.DeleteBucketHandler) + + /// Root operation + + // ListBuckets + apiRouter.Methods("GET").HandlerFunc(api.ListBucketsHandler) +} diff --git a/generic-handlers.go b/generic-handlers.go index a00d9f9ba..e25d78c18 100644 --- a/generic-handlers.go +++ b/generic-handlers.go @@ -46,13 +46,13 @@ type redirectHandler struct { locationPrefix string } -// Private bucket. +// Reserved bucket. const ( - privateBucket = "/minio" + reservedBucket = "/minio" ) func setBrowserRedirectHandler(h http.Handler) http.Handler { - return redirectHandler{handler: h, locationPrefix: privateBucket} + return redirectHandler{handler: h, locationPrefix: reservedBucket} } func (h redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -84,7 +84,7 @@ func setBrowserCacheControlHandler(h http.Handler) http.Handler { func (h cacheControlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" && strings.Contains(r.Header.Get("User-Agent"), "Mozilla") { // For all browser requests set appropriate Cache-Control policies - match, e := regexp.MatchString(privateBucket+`/([^/]+\.js|favicon.ico)`, r.URL.Path) + match, e := regexp.MatchString(reservedBucket+`/([^/]+\.js|favicon.ico)`, r.URL.Path) if e != nil { writeErrorResponse(w, r, ErrInternalError, r.URL.Path) return @@ -93,7 +93,7 @@ func (h cacheControlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // For assets set cache expiry of one year. For each release, the name // of the asset name will change and hence it can not be served from cache. w.Header().Set("Cache-Control", "max-age=31536000") - } else if strings.HasPrefix(r.URL.Path, privateBucket+"/") { + } else if strings.HasPrefix(r.URL.Path, reservedBucket+"/") { // For non asset requests we serve index.html which will never be cached. w.Header().Set("Cache-Control", "no-store") } @@ -103,17 +103,17 @@ func (h cacheControlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Adds verification for incoming paths. type minioPrivateBucketHandler struct { - handler http.Handler - privateBucket string + handler http.Handler + reservedBucket string } func setPrivateBucketHandler(h http.Handler) http.Handler { - return minioPrivateBucketHandler{handler: h, privateBucket: privateBucket} + return minioPrivateBucketHandler{handler: h, reservedBucket: reservedBucket} } func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // For all non browser requests, reject access to 'privateBucket'. - if !strings.Contains(r.Header.Get("User-Agent"), "Mozilla") && path.Clean(r.URL.Path) == privateBucket { + // For all non browser requests, reject access to 'reservedBucket'. + if !strings.Contains(r.Header.Get("User-Agent"), "Mozilla") && path.Clean(r.URL.Path) == reservedBucket { writeErrorResponse(w, r, ErrAllAccessDisabled, r.URL.Path) return } diff --git a/routers.go b/routers.go index 86a5fa453..4dc655d52 100644 --- a/routers.go +++ b/routers.go @@ -17,144 +17,15 @@ package main import ( - "fmt" "net" "net/http" - "github.com/elazarl/go-bindata-assetfs" - "github.com/gorilla/handlers" router "github.com/gorilla/mux" - jsonrpc "github.com/gorilla/rpc/v2" - "github.com/gorilla/rpc/v2/json2" "github.com/minio/minio-go" "github.com/minio/minio/pkg/fs" "github.com/minio/minio/pkg/probe" - "github.com/minio/miniobrowser" ) -// storageAPI container for S3 compatible API. -type storageAPI struct { - // Filesystem instance. - Filesystem fs.Filesystem -} - -// webAPI container for Web API. -type webAPI struct { - // FSPath filesystem path. - FSPath string - // Minio client instance. - Client *minio.Client - - // private params. - apiAddress string // api destination address. - // credential kept to be used internally. - accessKeyID string - secretAccessKey string -} - -// indexHandler - Handler to serve index.html -type indexHandler struct { - handler http.Handler -} - -func (h indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - r.URL.Path = privateBucket + "/" - h.handler.ServeHTTP(w, r) -} - -const assetPrefix = "production" - -func assetFS() *assetfs.AssetFS { - return &assetfs.AssetFS{ - Asset: miniobrowser.Asset, - AssetDir: miniobrowser.AssetDir, - AssetInfo: miniobrowser.AssetInfo, - Prefix: assetPrefix, - } -} - -// specialAssets are files which are unique files not embedded inside index_bundle.js. -const specialAssets = "loader.css|logo.svg|firefox.png|safari.png|chrome.png|favicon.ico" - -// registerAPIHandlers - register all the handlers to their respective paths -func registerAPIHandlers(mux *router.Router, a storageAPI, w *webAPI) { - // Minio rpc router - minio := mux.NewRoute().PathPrefix(privateBucket).Subrouter() - - // Initialize json rpc handlers. - rpc := jsonrpc.NewServer() - codec := json2.NewCodec() - rpc.RegisterCodec(codec, "application/json") - rpc.RegisterCodec(codec, "application/json; charset=UTF-8") - rpc.RegisterService(w, "Web") - - // RPC handler at URI - /minio/rpc - minio.Path("/rpc").Handler(rpc) - // Serve all assets. - minio.Path(fmt.Sprintf("/{assets:[^/]+.js|%s}", specialAssets)).Handler(handlers.CompressHandler(http.StripPrefix(privateBucket, http.FileServer(assetFS())))) - // Serve index.html for rest of the requests - minio.Path("/{index:.*}").Handler(indexHandler{http.StripPrefix(privateBucket, http.FileServer(assetFS()))}) - - // API Router - api := mux.NewRoute().PathPrefix("/").Subrouter() - - // Bucket router - bucket := api.PathPrefix("/{bucket}").Subrouter() - - /// Object operations - - // HeadObject - bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(a.HeadObjectHandler) - // PutObjectPart - bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(a.PutObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") - // ListObjectPxarts - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(a.ListObjectPartsHandler).Queries("uploadId", "{uploadId:.*}") - // CompleteMultipartUpload - bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(a.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") - // NewMultipartUpload - bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(a.NewMultipartUploadHandler).Queries("uploads", "") - // AbortMultipartUpload - bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(a.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}") - // GetObject - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(a.GetObjectHandler) - // CopyObject - bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/).*?").HandlerFunc(a.CopyObjectHandler) - // PutObject - bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(a.PutObjectHandler) - // DeleteObject - bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(a.DeleteObjectHandler) - - /// Bucket operations - - // GetBucketLocation - bucket.Methods("GET").HandlerFunc(a.GetBucketLocationHandler).Queries("location", "") - // GetBucketPolicy - bucket.Methods("GET").HandlerFunc(a.GetBucketPolicyHandler).Queries("policy", "") - // ListMultipartUploads - bucket.Methods("GET").HandlerFunc(a.ListMultipartUploadsHandler).Queries("uploads", "") - // ListObjects - bucket.Methods("GET").HandlerFunc(a.ListObjectsHandler) - // PutBucketPolicy - bucket.Methods("PUT").HandlerFunc(a.PutBucketPolicyHandler).Queries("policy", "") - // PutBucket - bucket.Methods("PUT").HandlerFunc(a.PutBucketHandler) - // HeadBucket - bucket.Methods("HEAD").HandlerFunc(a.HeadBucketHandler) - // PostPolicy - bucket.Methods("POST").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(a.PostPolicyBucketHandler) - // DeleteMultipleObjects - bucket.Methods("POST").HandlerFunc(a.DeleteMultipleObjectsHandler) - // DeleteBucketPolicy - bucket.Methods("DELETE").HandlerFunc(a.DeleteBucketPolicyHandler).Queries("policy", "") - // DeleteBucket - bucket.Methods("DELETE").HandlerFunc(a.DeleteBucketHandler) - - /// Root operation - - // ListBuckets - api.Methods("GET").HandlerFunc(a.ListBucketsHandler) -} - // configureServer handler returns final handler for the http server. func configureServerHandler(filesystem fs.Filesystem) http.Handler { // Access credentials. @@ -190,6 +61,16 @@ func configureServerHandler(filesystem fs.Filesystem) http.Handler { secretAccessKey: cred.SecretAccessKey, } + // Initialize router. + mux := router.NewRouter() + + // Register all routers. + registerWebRouter(mux, web) + registerAPIRouter(mux, api) + // Add new routers here. + + // List of some generic handlers which are applied for all + // incoming requests. var handlerFns = []HandlerFunc{ // Redirect some pre-defined browser request paths to a static // location prefix. @@ -209,14 +90,9 @@ func configureServerHandler(filesystem fs.Filesystem) http.Handler { // routes them accordingly. Client receives a HTTP error for // invalid/unsupported signatures. setAuthHandler, + // Add new handlers here. } - // Initialize router. - mux := router.NewRouter() - - // Register all API handlers. - registerAPIHandlers(mux, api, web) - // Register rest of the handlers. return registerHandlers(mux, handlerFns...) } diff --git a/web-handlers.go b/web-handlers.go index 789e9198e..d65e1be0b 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -168,7 +168,7 @@ func (web *webAPI) ListBuckets(r *http.Request, args *ListBucketsArgs, reply *Li } for _, bucket := range buckets { // List all buckets which are not private. - if bucket.Name != path.Base(privateBucket) { + if bucket.Name != path.Base(reservedBucket) { reply.Buckets = append(reply.Buckets, BucketInfo{ Name: bucket.Name, CreationDate: bucket.CreationDate, diff --git a/web-router.go b/web-router.go new file mode 100644 index 000000000..d7f417470 --- /dev/null +++ b/web-router.go @@ -0,0 +1,95 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + "net/http" + + "github.com/elazarl/go-bindata-assetfs" + "github.com/gorilla/handlers" + router "github.com/gorilla/mux" + jsonrpc "github.com/gorilla/rpc/v2" + "github.com/gorilla/rpc/v2/json2" + "github.com/minio/minio-go" + "github.com/minio/miniobrowser" +) + +// webAPI container for Web API. +type webAPI struct { + // FSPath filesystem path. + FSPath string + // Minio client instance. + Client *minio.Client + + // private params. + apiAddress string // api destination address. + // credential kept to be used internally. + accessKeyID string + secretAccessKey string +} + +// indexHandler - Handler to serve index.html +type indexHandler struct { + handler http.Handler +} + +func (h indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + r.URL.Path = reservedBucket + "/" + h.handler.ServeHTTP(w, r) +} + +const assetPrefix = "production" + +func assetFS() *assetfs.AssetFS { + return &assetfs.AssetFS{ + Asset: miniobrowser.Asset, + AssetDir: miniobrowser.AssetDir, + AssetInfo: miniobrowser.AssetInfo, + Prefix: assetPrefix, + } +} + +// specialAssets are files which are unique files not embedded inside index_bundle.js. +const specialAssets = "loader.css|logo.svg|firefox.png|safari.png|chrome.png|favicon.ico" + +// registerWebRouter - registers web router for serving minio browser. +func registerWebRouter(mux *router.Router, web *webAPI) { + // Initialize a new json2 codec. + codec := json2.NewCodec() + + // Minio rpc router + webBrowserRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter() + + // Initialize json rpc handlers. + webRPC := jsonrpc.NewServer() + webRPC.RegisterCodec(codec, "application/json") + webRPC.RegisterCodec(codec, "application/json; charset=UTF-8") + webRPC.RegisterService(web, "Web") + + // RPC handler at URI - /minio/rpc + webBrowserRouter.Path("/rpc").Handler(webRPC) + + // Add compression for assets. + compressedAssets := handlers.CompressHandler(http.StripPrefix(reservedBucket, http.FileServer(assetFS()))) + + // Serve javascript files and favicon from assets. + webBrowserRouter.Path(fmt.Sprintf("/{assets:[^/]+.js|%s}", specialAssets)).Handler(compressedAssets) + + // Serve index.html for rest of the requests. + webBrowserRouter.Path("/{index:.*}").Handler(indexHandler{http.StripPrefix(reservedBucket, http.FileServer(assetFS()))}) +}