diff --git a/commands.go b/commands.go index 8d18cf80b..a613b766d 100644 --- a/commands.go +++ b/commands.go @@ -4,7 +4,8 @@ import ( "os/user" "github.com/minio/cli" - "github.com/minio/minio/pkg/api" + "github.com/minio/minio/pkg/server" + "github.com/minio/minio/pkg/server/api" ) func removeDuplicates(slice []string) []string { @@ -58,16 +59,29 @@ EXAMPLES: `, } +func getAPIServerConfig(c *cli.Context) api.Config { + certFile := c.GlobalString("cert") + keyFile := c.GlobalString("key") + if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") { + Fatalln("Both certificate and key are required to enable https.") + } + tls := (certFile != "" && keyFile != "") + return api.Config{ + Address: c.GlobalString("address"), + TLS: tls, + CertFile: certFile, + KeyFile: keyFile, + RateLimit: c.GlobalInt("ratelimit"), + } +} + func runServer(c *cli.Context) { _, err := user.Current() if err != nil { Fatalf("Unable to determine current user. Reason: %s\n", err) } - if len(c.Args()) < 1 { - cli.ShowCommandHelpAndExit(c, "server", 1) // last argument is exit code - } apiServerConfig := getAPIServerConfig(c) - if err := api.StartServer(apiServerConfig); err != nil { + if err := server.StartServices(apiServerConfig); err != nil { Fatalln(err) } } diff --git a/main.go b/main.go index 82429440b..a372a4975 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,6 @@ import ( "github.com/dustin/go-humanize" "github.com/minio/cli" - "github.com/minio/minio/pkg/api" "github.com/minio/minio/pkg/iodine" ) @@ -71,22 +70,6 @@ func init() { } } -func getAPIServerConfig(c *cli.Context) api.Config { - certFile := c.GlobalString("cert") - keyFile := c.GlobalString("key") - if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") { - Fatalln("Both certificate and key are required to enable https.") - } - tls := (certFile != "" && keyFile != "") - return api.Config{ - Address: c.GlobalString("address"), - TLS: tls, - CertFile: certFile, - KeyFile: keyFile, - RateLimit: c.GlobalInt("ratelimit"), - } -} - // Tries to get os/arch/platform specific information // Returns a map of current os/arch/platform/memstats func getSystemData() map[string]string { diff --git a/pkg/api/api-router.go b/pkg/api/api-router.go deleted file mode 100644 index b6750b28d..000000000 --- a/pkg/api/api-router.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Minimalist Object Storage, (C) 2014 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 api - -import router "github.com/gorilla/mux" - -type minioAPI struct{} - -// New api -func New(config Config) API { - var api = minioAPI{} - - mux := router.NewRouter() - mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") - mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET") - mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT") - mux.HandleFunc("/{bucket}", api.headBucketHandler).Methods("HEAD") - mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD") - mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT") - mux.HandleFunc("/{bucket}/{object:.*}", api.listObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET") - mux.HandleFunc("/{bucket}/{object:.*}", api.completeMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST") - mux.HandleFunc("/{bucket}/{object:.*}", api.newMultipartUploadHandler).Methods("POST") - mux.HandleFunc("/{bucket}/{object:.*}", api.abortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("DELETE") - mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET") - mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT") - - // not implemented yet - mux.HandleFunc("/{bucket}", api.deleteBucketHandler).Methods("DELETE") - - // unsupported API - mux.HandleFunc("/{bucket}/{object:.*}", api.deleteObjectHandler).Methods("DELETE") - - handler := validContentTypeHandler(mux) - handler = timeValidityHandler(handler) - handler = ignoreResourcesHandler(handler) - handler = validateAuthHeaderHandler(handler) - handler = rateLimitHandler(handler, config.RateLimit) - handler = loggingHandler(handler) - return API{config, handler} -} diff --git a/pkg/api/web/web.go b/pkg/api/web/web.go deleted file mode 100644 index 4e7ffeae9..000000000 --- a/pkg/api/web/web.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Minimalist Object Storage, (C) 2015 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 web - -import ( - "bytes" - "encoding/json" - "net/http" - "path/filepath" - - "github.com/gorilla/mux" - "github.com/minio/minio/pkg/api/config" - "github.com/minio/minio/pkg/iodine" - "github.com/minio/minio/pkg/utils/crypto/keys" - "github.com/minio/minio/pkg/utils/log" -) - -const ( - defaultWeb = "polygon" -) - -type webAPI struct { - conf config.Config - webPath string -} - -// No encoder interface exists, so we create one. -type encoder interface { - Encode(v interface{}) error -} - -// HTTPHandler - http wrapper handler -func HTTPHandler() http.Handler { - mux := mux.NewRouter() - var api = webAPI{} - - if err := api.conf.SetupConfig(); err != nil { - log.Fatal(iodine.New(err, nil)) - } - - api.webPath = filepath.Join(api.conf.GetConfigPath(), defaultWeb) - mux.Handle("/{polygon:.*}", http.FileServer(http.Dir(api.webPath))).Methods("GET") - mux.HandleFunc("/access", api.accessHandler).Methods("POST") - return mux -} - -func writeResponse(w http.ResponseWriter, response interface{}) []byte { - var bytesBuffer bytes.Buffer - var encoder encoder - w.Header().Set("Content-Type", "application/json") - encoder = json.NewEncoder(&bytesBuffer) - w.Header().Set("Server", "Minio Management Console") - w.Header().Set("Connection", "close") - encoder.Encode(response) - return bytesBuffer.Bytes() -} - -func (web *webAPI) accessHandler(w http.ResponseWriter, req *http.Request) { - var err error - var accesskey, secretkey []byte - username := req.FormValue("username") - if len(username) <= 0 { - w.WriteHeader(http.StatusBadRequest) - return - } - - err = web.conf.ReadConfig() - if err != nil { - log.Error.Println(iodine.New(err, nil)) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - if web.conf.IsUserExists(username) { - w.WriteHeader(http.StatusConflict) - return - } - - var user = config.User{} - user.Name = username - - accesskey, err = keys.GenerateRandomAlphaNumeric(keys.MinioAccessID) - if err != nil { - log.Error.Println(iodine.New(err, nil)) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - user.AccessKey = string(accesskey) - - secretkey, err = keys.GenerateRandomBase64(keys.MinioSecretID) - if err != nil { - log.Error.Println(iodine.New(err, nil)) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - user.SecretKey = string(secretkey) - - web.conf.AddUser(user) - err = web.conf.WriteConfig() - if err != nil { - log.Error.Println(iodine.New(err, nil)) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - err = web.conf.ReadConfig() - if err != nil { - log.Error.Println(iodine.New(err, nil)) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - // Get user back for sending it over HTTP reply - user = web.conf.GetUser(username) - w.Write(writeResponse(w, user)) -} diff --git a/pkg/api/acl.go b/pkg/server/api/acl.go similarity index 100% rename from pkg/api/acl.go rename to pkg/server/api/acl.go diff --git a/pkg/api/api-bucket-handlers.go b/pkg/server/api/api-bucket-handlers.go similarity index 79% rename from pkg/api/api-bucket-handlers.go rename to pkg/server/api/api-bucket-handlers.go index cec239e98..bae237e0b 100644 --- a/pkg/api/api-bucket-handlers.go +++ b/pkg/server/api/api-bucket-handlers.go @@ -23,21 +23,24 @@ import ( "github.com/gorilla/mux" ) -func (server *minioAPI) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool { +// MinioAPI - +type MinioAPI struct{} + +func (api MinioAPI) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool { vars := mux.Vars(req) bucket := vars["bucket"] log.Println(bucket) return true } -// GET Bucket (List Multipart uploads) +// ListMultipartUploadsHandler - GET Bucket (List Multipart uploads) // ------------------------- // This operation lists in-progress multipart uploads. An in-progress // multipart upload is a multipart upload that has been initiated, // using the Initiate Multipart Upload request, but has not yet been completed or aborted. // This operation returns at most 1,000 multipart uploads in the response. // -func (server *minioAPI) listMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) log.Println(acceptsContentType) @@ -51,21 +54,21 @@ func (server *minioAPI) listMultipartUploadsHandler(w http.ResponseWriter, req * log.Println(bucket) } -// GET Bucket (List Objects) +// ListObjectsHandler - GET Bucket (List Objects) // ------------------------- // This implementation of the GET operation returns some or all (up to 1000) // of the objects in a bucket. You can use the request parameters as selection // criteria to return a subset of the objects in a bucket. // -func (server *minioAPI) listObjectsHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) ListObjectsHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // verify if bucket allows this operation - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } if isRequestUploads(req.URL.Query()) { - server.listMultipartUploadsHandler(w, req) + api.ListMultipartUploadsHandler(w, req) return } @@ -80,11 +83,11 @@ func (server *minioAPI) listObjectsHandler(w http.ResponseWriter, req *http.Requ } -// GET Service +// ListBucketsHandler - GET Service // ----------- // This implementation of the GET operation returns a list of all buckets // owned by the authenticated sender of the request. -func (server *minioAPI) listBucketsHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) ListBucketsHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // uncomment this when we have webcli // without access key credentials one cannot list buckets @@ -95,10 +98,10 @@ func (server *minioAPI) listBucketsHandler(w http.ResponseWriter, req *http.Requ log.Println(acceptsContentType) } -// PUT Bucket +// PutBucketHandler - PUT Bucket // ---------- // This implementation of the PUT operation creates a new bucket for authenticated request -func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) PutBucketHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // uncomment this when we have webcli // without access key credentials one cannot create a bucket @@ -107,7 +110,7 @@ func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Reques // return // } if isRequestBucketACL(req.URL.Query()) { - server.putBucketACLHandler(w, req) + api.PutBucketACLHandler(w, req) return } // read from 'x-amz-acl' @@ -122,10 +125,10 @@ func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Reques log.Println(bucket) } -// PUT Bucket ACL +// PutBucketACLHandler - PUT Bucket ACL // ---------- // This implementation of the PUT operation modifies the bucketACL for authenticated request -func (server *minioAPI) putBucketACLHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // read from 'x-amz-acl' aclType := getACLType(req) @@ -139,13 +142,13 @@ func (server *minioAPI) putBucketACLHandler(w http.ResponseWriter, req *http.Req log.Println(bucket) } -// HEAD Bucket +// HeadBucketHandler - HEAD Bucket // ---------- // This operation is useful to determine if a bucket exists. // The operation returns a 200 OK if the bucket exists and you // have permission to access it. Otherwise, the operation might // return responses such as 404 Not Found and 403 Forbidden. -func (server *minioAPI) headBucketHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) HeadBucketHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) log.Println(acceptsContentType) diff --git a/pkg/api/api-definitions.go b/pkg/server/api/api-definitions.go similarity index 97% rename from pkg/api/api-definitions.go rename to pkg/server/api/api-definitions.go index 526bfba6f..e4b2ffbdb 100644 --- a/pkg/api/api-definitions.go +++ b/pkg/server/api/api-definitions.go @@ -18,6 +18,15 @@ package api import "encoding/xml" +// Config - http server config +type Config struct { + Address string + TLS bool + CertFile string + KeyFile string + RateLimit int +} + // Limit number of objects in a given response const ( maxObjectList = 1000 diff --git a/pkg/api/api-generic-handlers.go b/pkg/server/api/api-generic-handlers.go similarity index 94% rename from pkg/api/api-generic-handlers.go rename to pkg/server/api/api-generic-handlers.go index 2f0cf251e..1cff82502 100644 --- a/pkg/api/api-generic-handlers.go +++ b/pkg/server/api/api-generic-handlers.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/minio/minio/pkg/api/config" + "github.com/minio/minio/pkg/server/config" "github.com/minio/minio/pkg/utils/crypto/keys" ) @@ -128,7 +128,8 @@ func parseDate(req *http.Request) (time.Time, error) { return time.Time{}, errors.New("invalid request") } -func validContentTypeHandler(h http.Handler) http.Handler { +// ValidContentTypeHandler - +func ValidContentTypeHandler(h http.Handler) http.Handler { return contentTypeHandler{h} } @@ -141,7 +142,8 @@ func (h contentTypeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.handler.ServeHTTP(w, r) } -func timeValidityHandler(h http.Handler) http.Handler { +// TimeValidityHandler - +func TimeValidityHandler(h http.Handler) http.Handler { return timeHandler{h} } @@ -170,9 +172,10 @@ func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.handler.ServeHTTP(w, r) } +// ValidateAuthHeaderHandler - // validate auth header handler is wrapper handler used for API request validation with authorization header. // Current authorization layer supports S3's standard HMAC based signature request. -func validateAuthHeaderHandler(h http.Handler) http.Handler { +func ValidateAuthHeaderHandler(h http.Handler) http.Handler { return validateAuthHandler{h} } @@ -206,10 +209,11 @@ func (h validateAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +// IgnoreResourcesHandler - // Ignore resources handler is wrapper handler used for API request resource validation // Since we do not support all the S3 queries, it is necessary for us to throw back a // valid error message indicating such a feature is not implemented. -func ignoreResourcesHandler(h http.Handler) http.Handler { +func IgnoreResourcesHandler(h http.Handler) http.Handler { return resourceHandler{h} } diff --git a/pkg/api/api-logging-handlers.go b/pkg/server/api/api-logging-handlers.go similarity index 97% rename from pkg/api/api-logging-handlers.go rename to pkg/server/api/api-logging-handlers.go index 87e12b98d..0ee9ca2fe 100644 --- a/pkg/api/api-logging-handlers.go +++ b/pkg/server/api/api-logging-handlers.go @@ -94,8 +94,8 @@ func getLogMessage(logMessage *logMessage, w http.ResponseWriter, req *http.Requ return js } -// loggingHandler logs requests -func loggingHandler(h http.Handler) http.Handler { +// LoggingHandler logs requests +func LoggingHandler(h http.Handler) http.Handler { logger, _ := fileLogger("access.log") return &logHandler{handler: h, logger: logger} } diff --git a/pkg/api/api-object-handlers.go b/pkg/server/api/api-object-handlers.go similarity index 78% rename from pkg/api/api-object-handlers.go rename to pkg/server/api/api-object-handlers.go index 409c4d571..0ec281500 100644 --- a/pkg/api/api-object-handlers.go +++ b/pkg/server/api/api-object-handlers.go @@ -32,14 +32,14 @@ const ( maxPartsList = 1000 ) -// GET Object +// GetObjectHandler - GET Object // ---------- // This implementation of the GET operation retrieves object. To use GET, // you must have READ access to the object. -func (server *minioAPI) getObjectHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) GetObjectHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // verify if this operation is allowed - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -51,13 +51,13 @@ func (server *minioAPI) getObjectHandler(w http.ResponseWriter, req *http.Reques } -// HEAD Object +// HeadObjectHandler - HEAD Object // ----------- // The HEAD operation retrieves metadata from an object without returning the object itself. -func (server *minioAPI) headObjectHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) HeadObjectHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // verify if this operation is allowed - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -68,13 +68,13 @@ func (server *minioAPI) headObjectHandler(w http.ResponseWriter, req *http.Reque log.Println(bucket, object) } -// PUT Object +// PutObjectHandler - PUT Object // ---------- // This implementation of the PUT operation adds an object to a bucket. -func (server *minioAPI) putObjectHandler(w http.ResponseWriter, req *http.Request) { +func (api MinioAPI) PutObjectHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // verify if this operation is allowed - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -120,11 +120,11 @@ func (server *minioAPI) putObjectHandler(w http.ResponseWriter, req *http.Reques /// Multipart API -// New multipart upload -func (server *minioAPI) newMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { +// NewMultipartUploadHandler - New multipart upload +func (api MinioAPI) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // handle ACL's here at bucket level - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -140,11 +140,11 @@ func (server *minioAPI) newMultipartUploadHandler(w http.ResponseWriter, req *ht log.Println(bucket, object) } -// Upload part -func (server *minioAPI) putObjectPartHandler(w http.ResponseWriter, req *http.Request) { +// PutObjectPartHandler - Upload part +func (api MinioAPI) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // handle ACL's here at bucket level - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -189,11 +189,11 @@ func (server *minioAPI) putObjectPartHandler(w http.ResponseWriter, req *http.Re log.Println(uploadID, partID) } -// Abort multipart upload -func (server *minioAPI) abortMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { +// AbortMultipartUploadHandler - Abort multipart upload +func (api MinioAPI) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // handle ACL's here at bucket level - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -205,11 +205,11 @@ func (server *minioAPI) abortMultipartUploadHandler(w http.ResponseWriter, req * log.Println(bucket, object) } -// List object parts -func (server *minioAPI) listObjectPartsHandler(w http.ResponseWriter, req *http.Request) { +// ListObjectPartsHandler - List object parts +func (api MinioAPI) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // handle ACL's here at bucket level - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -224,11 +224,11 @@ func (server *minioAPI) listObjectPartsHandler(w http.ResponseWriter, req *http. log.Println(bucket, object) } -// Complete multipart upload -func (server *minioAPI) completeMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { +// CompleteMultipartUploadHandler - Complete multipart upload +func (api MinioAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http.Request) { acceptsContentType := getContentType(req) // handle ACL's here at bucket level - if !server.isValidOp(w, req, acceptsContentType) { + if !api.isValidOp(w, req, acceptsContentType) { return } @@ -261,14 +261,14 @@ func (server *minioAPI) completeMultipartUploadHandler(w http.ResponseWriter, re /// Delete API -// Delete bucket -func (server *minioAPI) deleteBucketHandler(w http.ResponseWriter, req *http.Request) { +// DeleteBucketHandler - Delete bucket +func (api MinioAPI) DeleteBucketHandler(w http.ResponseWriter, req *http.Request) { error := getErrorCode(NotImplemented) w.WriteHeader(error.HTTPStatusCode) } -// Delete object -func (server *minioAPI) deleteObjectHandler(w http.ResponseWriter, req *http.Request) { +// DeleteObjectHandler - Delete object +func (api MinioAPI) DeleteObjectHandler(w http.ResponseWriter, req *http.Request) { error := getErrorCode(NotImplemented) w.WriteHeader(error.HTTPStatusCode) } diff --git a/pkg/api/api-ratelimit-handlers.go b/pkg/server/api/api-ratelimit-handlers.go similarity index 90% rename from pkg/api/api-ratelimit-handlers.go rename to pkg/server/api/api-ratelimit-handlers.go index 03e31102c..ab35284de 100644 --- a/pkg/api/api-ratelimit-handlers.go +++ b/pkg/server/api/api-ratelimit-handlers.go @@ -41,8 +41,8 @@ func (c rateLimit) ServeHTTP(w http.ResponseWriter, req *http.Request) { c.Remove() // remove } -// rateLimitHandler limits the number of concurrent http requests -func rateLimitHandler(handle http.Handler, limit int) http.Handler { +// RateLimitHandler limits the number of concurrent http requests +func RateLimitHandler(handle http.Handler, limit int) http.Handler { return rateLimit{ handler: handle, rateQueue: make(chan bool, limit), diff --git a/pkg/api/api-response.go b/pkg/server/api/api-response.go similarity index 100% rename from pkg/api/api-response.go rename to pkg/server/api/api-response.go diff --git a/pkg/api/contenttype.go b/pkg/server/api/contenttype.go similarity index 100% rename from pkg/api/contenttype.go rename to pkg/server/api/contenttype.go diff --git a/pkg/api/errors.go b/pkg/server/api/errors.go similarity index 100% rename from pkg/api/errors.go rename to pkg/server/api/errors.go diff --git a/pkg/api/headers.go b/pkg/server/api/headers.go similarity index 100% rename from pkg/api/headers.go rename to pkg/server/api/headers.go diff --git a/pkg/api/range.go b/pkg/server/api/range.go similarity index 100% rename from pkg/api/range.go rename to pkg/server/api/range.go diff --git a/pkg/api/resources.go b/pkg/server/api/resources.go similarity index 100% rename from pkg/api/resources.go rename to pkg/server/api/resources.go diff --git a/pkg/api/utils.go b/pkg/server/api/utils.go similarity index 100% rename from pkg/api/utils.go rename to pkg/server/api/utils.go diff --git a/pkg/api/config/config.go b/pkg/server/config/config.go similarity index 100% rename from pkg/api/config/config.go rename to pkg/server/config/config.go diff --git a/pkg/api/config/config_test.go b/pkg/server/config/config_test.go similarity index 100% rename from pkg/api/config/config_test.go rename to pkg/server/config/config_test.go diff --git a/pkg/server/router.go b/pkg/server/router.go new file mode 100644 index 000000000..416f45da0 --- /dev/null +++ b/pkg/server/router.go @@ -0,0 +1,77 @@ +/* + * Minimalist Object Storage, (C) 2014 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 server + +import ( + "net/http" + + router "github.com/gorilla/mux" + jsonRPC "github.com/gorilla/rpc/v2" + "github.com/minio/minio/pkg/server/api" + "github.com/minio/minio/pkg/server/rpc" +) + +func registerAPI(mux *router.Router) http.Handler { + api := api.MinioAPI{} + + mux.HandleFunc("/", api.ListBucketsHandler).Methods("GET") + mux.HandleFunc("/{bucket}", api.ListObjectsHandler).Methods("GET") + mux.HandleFunc("/{bucket}", api.PutBucketHandler).Methods("PUT") + mux.HandleFunc("/{bucket}", api.HeadBucketHandler).Methods("HEAD") + mux.HandleFunc("/{bucket}/{object:.*}", api.HeadObjectHandler).Methods("HEAD") + mux.HandleFunc("/{bucket}/{object:.*}", api.PutObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT") + mux.HandleFunc("/{bucket}/{object:.*}", api.ListObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET") + mux.HandleFunc("/{bucket}/{object:.*}", api.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST") + mux.HandleFunc("/{bucket}/{object:.*}", api.NewMultipartUploadHandler).Methods("POST") + mux.HandleFunc("/{bucket}/{object:.*}", api.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("DELETE") + mux.HandleFunc("/{bucket}/{object:.*}", api.GetObjectHandler).Methods("GET") + mux.HandleFunc("/{bucket}/{object:.*}", api.PutObjectHandler).Methods("PUT") + + // not implemented yet + mux.HandleFunc("/{bucket}", api.DeleteBucketHandler).Methods("DELETE") + + // unsupported API + mux.HandleFunc("/{bucket}/{object:.*}", api.DeleteObjectHandler).Methods("DELETE") + + return mux +} + +func registerOthers(mux http.Handler, conf api.Config) http.Handler { + mux = api.ValidContentTypeHandler(mux) + mux = api.TimeValidityHandler(mux) + mux = api.IgnoreResourcesHandler(mux) + mux = api.ValidateAuthHeaderHandler(mux) + mux = api.RateLimitHandler(mux, conf.RateLimit) + mux = api.LoggingHandler(mux) + return mux +} + +func registerRPC(mux *router.Router, r *jsonRPC.Server) http.Handler { + mux.Handle("/rpc", r) + return mux +} + +// APIHandler api handler +func APIHandler(conf api.Config) http.Handler { + mux := router.NewRouter() + return registerOthers(registerAPI(mux), conf) +} + +// RPCHandler rpc handler +func RPCHandler() http.Handler { + return registerRPC(router.NewRouter(), rpc.HelloServiceHandler()) +} diff --git a/pkg/server/rpc/methods.go b/pkg/server/rpc/methods.go new file mode 100644 index 000000000..4c654264b --- /dev/null +++ b/pkg/server/rpc/methods.go @@ -0,0 +1,38 @@ +/* + * Minimalist Object Storage, (C) 2015 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 rpc + +import "net/http" + +// HelloArgs - hello args +type HelloArgs struct { + Who string +} + +// HelloReply - hello reply +type HelloReply struct { + Message string +} + +// HelloService - +type HelloService struct{} + +// Say - +func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error { + reply.Message = "Hello, " + args.Who + "!" + return nil +} diff --git a/pkg/server/rpc/server.go b/pkg/server/rpc/server.go new file mode 100644 index 000000000..2b98b5213 --- /dev/null +++ b/pkg/server/rpc/server.go @@ -0,0 +1,30 @@ +/* + * Minimalist Object Storage, (C) 2015 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 rpc + +import ( + "github.com/gorilla/rpc/v2" + "github.com/gorilla/rpc/v2/json" +) + +// HelloServiceHandler - +func HelloServiceHandler() *rpc.Server { + s := rpc.NewServer() + s.RegisterCodec(json.NewCodec(), "application/json") + s.RegisterService(new(HelloService), "") + return s +} diff --git a/pkg/api/server.go b/pkg/server/server.go similarity index 63% rename from pkg/api/server.go rename to pkg/server/server.go index 4aaf179b8..bf76caa06 100644 --- a/pkg/api/server.go +++ b/pkg/server/server.go @@ -14,43 +14,29 @@ * limitations under the License. */ -package api +package server import ( "fmt" "net" "net/http" "strings" + + "github.com/minio/minio/pkg/server/api" ) -// Config - http server config -type Config struct { - Address string - TLS bool - CertFile string - KeyFile string - RateLimit int -} - -// Start http server -func Start(a API) <-chan error { - errCh := make(chan error) - go start(errCh, a) - return errCh -} - -func start(errCh chan error, a API) { +func startAPI(errCh chan error, conf api.Config) { defer close(errCh) var err error // Minio server config httpServer := &http.Server{ - Addr: a.config.Address, - Handler: a.handler, + Addr: conf.Address, + Handler: APIHandler(conf), MaxHeaderBytes: 1 << 20, } - host, port, err := net.SplitHostPort(a.config.Address) + host, port, err := net.SplitHostPort(conf.Address) if err != nil { errCh <- err return @@ -81,11 +67,11 @@ func start(errCh chan error, a API) { fmt.Printf("Starting minio server on: http://%s:%s\n", host, port) } err = httpServer.ListenAndServe() - case a.config.TLS == true: + case conf.TLS == true: for _, host := range hosts { fmt.Printf("Starting minio server on: https://%s:%s\n", host, port) } - err = httpServer.ListenAndServeTLS(a.config.CertFile, a.config.KeyFile) + err = httpServer.ListenAndServeTLS(conf.CertFile, conf.KeyFile) } if err != nil { errCh <- err @@ -94,15 +80,39 @@ func start(errCh chan error, a API) { return } -// API is used to build api server -type API struct { - config Config - handler http.Handler +func startRPC(errCh chan error) { + defer close(errCh) + + rpcHandler := RPCHandler() + var err error + // Minio server config + httpServer := &http.Server{ + Addr: "127.0.0.1:9001", + Handler: rpcHandler, + MaxHeaderBytes: 1 << 20, + } + err = httpServer.ListenAndServe() + if err != nil { + errCh <- err + } + errCh <- nil + return } -// StartServer APIFactory builds api server -func StartServer(conf Config) error { - for err := range Start(New(conf)) { +// StartServices starts basic services for a server +func StartServices(conf api.Config) error { + apiErrCh := make(chan error) + rpcErrCh := make(chan error) + + go startAPI(apiErrCh, conf) + go startRPC(rpcErrCh) + + select { + case err := <-apiErrCh: + if err != nil { + return err + } + case err := <-rpcErrCh: if err != nil { return err }