Remove some api server code bringing in new cleanup

master
Harshavardhana 10 years ago
parent c2031ca066
commit 72572d6c71
  1. 155
      pkg/api/api-bucket-handlers.go
  2. 0
      pkg/api/api-definitions.go
  3. 4
      pkg/api/api-generic-handlers.go
  4. 54
      pkg/api/api-logging-handlers.go
  5. 229
      pkg/api/api-object-handlers.go
  6. 14
      pkg/api/api-ratelimit-handlers.go
  7. 0
      pkg/api/api-response.go
  8. 48
      pkg/api/api-router.go
  9. 320
      pkg/api/api_bucket_handlers.go
  10. 1793
      pkg/api/api_test.go
  11. 152
      pkg/api/quota/bandwidth_cap.go
  12. 89
      pkg/api/quota/conn_limit.go
  13. 127
      pkg/api/quota/errors.go
  14. 96
      pkg/api/quota/quota_handler.go
  15. 65
      pkg/api/quota/request_limit.go
  16. 58
      pkg/api/server.go
  17. 129
      pkg/server/server.go

@ -0,0 +1,155 @@
/*
* 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 api
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func (server *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)
// -------------------------
// 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) {
acceptsContentType := getContentType(req)
log.Println(acceptsContentType)
resources := getBucketMultipartResources(req.URL.Query())
if resources.MaxUploads == 0 {
resources.MaxUploads = maxObjectList
}
vars := mux.Vars(req)
bucket := vars["bucket"]
log.Println(bucket)
}
// 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) {
acceptsContentType := getContentType(req)
// verify if bucket allows this operation
if !server.isValidOp(w, req, acceptsContentType) {
return
}
if isRequestUploads(req.URL.Query()) {
server.listMultipartUploadsHandler(w, req)
return
}
resources := getBucketResources(req.URL.Query())
if resources.Maxkeys == 0 {
resources.Maxkeys = maxObjectList
}
vars := mux.Vars(req)
bucket := vars["bucket"]
log.Println(bucket)
}
// 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) {
acceptsContentType := getContentType(req)
// uncomment this when we have webcli
// without access key credentials one cannot list buckets
// if _, err := stripAuth(req); err != nil {
// writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
// return
// }
log.Println(acceptsContentType)
}
// 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) {
acceptsContentType := getContentType(req)
// uncomment this when we have webcli
// without access key credentials one cannot create a bucket
// if _, err := stripAuth(req); err != nil {
// writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
// return
// }
if isRequestBucketACL(req.URL.Query()) {
server.putBucketACLHandler(w, req)
return
}
// read from 'x-amz-acl'
aclType := getACLType(req)
if aclType == unsupportedACLType {
writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
return
}
vars := mux.Vars(req)
bucket := vars["bucket"]
log.Println(bucket)
}
// 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) {
acceptsContentType := getContentType(req)
// read from 'x-amz-acl'
aclType := getACLType(req)
if aclType == unsupportedACLType {
writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
return
}
vars := mux.Vars(req)
bucket := vars["bucket"]
log.Println(bucket)
}
// 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) {
acceptsContentType := getContentType(req)
log.Println(acceptsContentType)
vars := mux.Vars(req)
bucket := vars["bucket"]
log.Println(bucket)
}

@ -98,7 +98,7 @@ func stripAuth(r *http.Request) (*auth, error) {
return a, nil
}
func getDate(req *http.Request) (time.Time, error) {
func parseDate(req *http.Request) (time.Time, error) {
amzDate := req.Header.Get("X-Amz-Date")
switch {
case amzDate != "":
@ -154,7 +154,7 @@ func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path)
return
}
date, err := getDate(r)
date, err := parseDate(r)
if err != nil {
// there is no way to knowing if this is a valid request, could be a attack reject such clients
writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path)

@ -14,7 +14,7 @@
* limitations under the License.
*/
package logging
package api
import (
"bytes"
@ -31,12 +31,12 @@ import (
)
type logHandler struct {
http.Handler
Logger chan<- []byte
handler http.Handler
logger chan<- []byte
}
// LogMessage is a serializable json log message
type LogMessage struct {
// logMessage is a serializable json log message
type logMessage struct {
StartTime time.Time
Duration time.Duration
StatusMessage string // human readable http status message
@ -49,38 +49,38 @@ type LogMessage struct {
}
}
// LogWriter is used to capture status for log messages
type LogWriter struct {
ResponseWriter http.ResponseWriter
LogMessage *LogMessage
// logWriter is used to capture status for log messages
type logWriter struct {
responseWriter http.ResponseWriter
logMessage *logMessage
}
// WriteHeader writes headers and stores status in LogMessage
func (w *LogWriter) WriteHeader(status int) {
w.LogMessage.StatusMessage = http.StatusText(status)
w.ResponseWriter.WriteHeader(status)
func (w *logWriter) WriteHeader(status int) {
w.logMessage.StatusMessage = http.StatusText(status)
w.responseWriter.WriteHeader(status)
}
// Header Dummy wrapper for LogWriter
func (w *LogWriter) Header() http.Header {
return w.ResponseWriter.Header()
func (w *logWriter) Header() http.Header {
return w.responseWriter.Header()
}
// Write Dummy wrapper for LogWriter
func (w *LogWriter) Write(data []byte) (int, error) {
return w.ResponseWriter.Write(data)
func (w *logWriter) Write(data []byte) (int, error) {
return w.responseWriter.Write(data)
}
func (h *logHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
logMessage := &LogMessage{
logMessage := &logMessage{
StartTime: time.Now().UTC(),
}
logWriter := &LogWriter{ResponseWriter: w, LogMessage: logMessage}
h.Handler.ServeHTTP(logWriter, req)
h.Logger <- getLogMessage(logMessage, w, req)
logWriter := &logWriter{responseWriter: w, logMessage: logMessage}
h.handler.ServeHTTP(logWriter, req)
h.logger <- getLogMessage(logMessage, w, req)
}
func getLogMessage(logMessage *LogMessage, w http.ResponseWriter, req *http.Request) []byte {
func getLogMessage(logMessage *logMessage, w http.ResponseWriter, req *http.Request) []byte {
// store lower level details
logMessage.HTTP.ResponseHeaders = w.Header()
logMessage.HTTP.Request = req
@ -94,14 +94,14 @@ func getLogMessage(logMessage *LogMessage, w http.ResponseWriter, req *http.Requ
return js
}
// LogHandler logs requests
func LogHandler(h http.Handler) http.Handler {
logger, _ := FileLogger("access.log")
return &logHandler{Handler: h, Logger: logger}
// loggingHandler logs requests
func loggingHandler(h http.Handler) http.Handler {
logger, _ := fileLogger("access.log")
return &logHandler{handler: h, logger: logger}
}
// FileLogger returns a channel that is used to write to the logger
func FileLogger(filename string) (chan<- []byte, error) {
// fileLogger returns a channel that is used to write to the logger
func fileLogger(filename string) (chan<- []byte, error) {
ch := make(chan []byte)
file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {

@ -25,7 +25,6 @@ import (
"github.com/gorilla/mux"
"github.com/minio/minio/pkg/iodine"
"github.com/minio/minio/pkg/storage/drivers"
"github.com/minio/minio/pkg/utils/log"
)
@ -48,47 +47,8 @@ func (server *minioAPI) getObjectHandler(w http.ResponseWriter, req *http.Reques
vars := mux.Vars(req)
bucket = vars["bucket"]
object = vars["object"]
log.Println(bucket, object)
metadata, err := server.driver.GetObjectMetadata(bucket, object)
switch iodine.ToError(err).(type) {
case nil: // success
{
httpRange, err := getRequestedRange(req, metadata.Size)
if err != nil {
writeErrorResponse(w, req, InvalidRange, acceptsContentType, req.URL.Path)
return
}
switch httpRange.start == 0 && httpRange.length == 0 {
case true:
setObjectHeaders(w, metadata)
if _, err := server.driver.GetObject(w, bucket, object); err != nil {
// unable to write headers, we've already printed data. Just close the connection.
log.Error.Println(iodine.New(err, nil))
}
case false:
metadata.Size = httpRange.length
setRangeObjectHeaders(w, metadata, httpRange)
w.WriteHeader(http.StatusPartialContent)
if _, err := server.driver.GetPartialObject(w, bucket, object, httpRange.start, httpRange.length); err != nil {
// unable to write headers, we've already printed data. Just close the connection.
log.Error.Println(iodine.New(err, nil))
}
}
}
case drivers.ObjectNotFound:
{
writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
}
case drivers.ObjectNameInvalid:
{
writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// HEAD Object
@ -105,34 +65,7 @@ func (server *minioAPI) headObjectHandler(w http.ResponseWriter, req *http.Reque
vars := mux.Vars(req)
bucket = vars["bucket"]
object = vars["object"]
metadata, err := server.driver.GetObjectMetadata(bucket, object)
switch iodine.ToError(err).(type) {
case nil:
{
setObjectHeaders(w, metadata)
w.WriteHeader(http.StatusOK)
}
case drivers.ObjectNotFound:
{
error := getErrorCode(NoSuchKey)
w.Header().Set("Server", "Minio")
w.WriteHeader(error.HTTPStatusCode)
}
case drivers.ObjectNameInvalid:
{
error := getErrorCode(NoSuchKey)
w.Header().Set("Server", "Minio")
w.WriteHeader(error.HTTPStatusCode)
}
default:
{
log.Error.Println(iodine.New(err, nil))
error := getErrorCode(InternalError)
w.Header().Set("Server", "Minio")
w.WriteHeader(error.HTTPStatusCode)
}
}
log.Println(bucket, object)
}
// PUT Object
@ -182,36 +115,7 @@ func (server *minioAPI) putObjectHandler(w http.ResponseWriter, req *http.Reques
writeErrorResponse(w, req, InvalidRequest, acceptsContentType, req.URL.Path)
return
}
calculatedMD5, err := server.driver.CreateObject(bucket, object, "", md5, sizeInt64, req.Body)
switch iodine.ToError(err).(type) {
case nil:
{
w.Header().Set("ETag", calculatedMD5)
writeSuccessResponse(w, acceptsContentType)
}
case drivers.ObjectExists:
{
writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
}
case drivers.BadDigest:
{
writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path)
}
case drivers.EntityTooLarge:
{
writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
}
case drivers.InvalidDigest:
{
writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
log.Println(bucket, object, sizeInt64)
}
/// Multipart API
@ -233,27 +137,7 @@ func (server *minioAPI) newMultipartUploadHandler(w http.ResponseWriter, req *ht
vars := mux.Vars(req)
bucket = vars["bucket"]
object = vars["object"]
uploadID, err := server.driver.NewMultipartUpload(bucket, object, "")
switch iodine.ToError(err).(type) {
case nil:
{
response := generateInitiateMultipartUploadResult(bucket, object, uploadID)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
case drivers.ObjectExists:
{
writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
log.Println(bucket, object)
}
// Upload part
@ -293,6 +177,7 @@ func (server *minioAPI) putObjectPartHandler(w http.ResponseWriter, req *http.Re
vars := mux.Vars(req)
bucket := vars["bucket"]
object := vars["object"]
log.Println(bucket, object, sizeInt64)
uploadID := req.URL.Query().Get("uploadId")
partIDString := req.URL.Query().Get("partNumber")
@ -301,40 +186,7 @@ func (server *minioAPI) putObjectPartHandler(w http.ResponseWriter, req *http.Re
if err != nil {
writeErrorResponse(w, req, InvalidPart, acceptsContentType, req.URL.Path)
}
calculatedMD5, err := server.driver.CreateObjectPart(bucket, object, uploadID, partID, "", md5, sizeInt64, req.Body)
switch iodine.ToError(err).(type) {
case nil:
{
w.Header().Set("ETag", calculatedMD5)
writeSuccessResponse(w, acceptsContentType)
}
case drivers.InvalidUploadID:
{
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
}
case drivers.ObjectExists:
{
writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
}
case drivers.BadDigest:
{
writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path)
}
case drivers.EntityTooLarge:
{
writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
}
case drivers.InvalidDigest:
{
writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
log.Println(uploadID, partID)
}
// Abort multipart upload
@ -349,25 +201,8 @@ func (server *minioAPI) abortMultipartUploadHandler(w http.ResponseWriter, req *
bucket := vars["bucket"]
object := vars["object"]
objectResourcesMetadata := getObjectResources(req.URL.Query())
err := server.driver.AbortMultipartUpload(bucket, object, objectResourcesMetadata.UploadID)
switch iodine.ToError(err).(type) {
case nil:
{
setCommonHeaders(w, getContentTypeString(acceptsContentType), 0)
w.WriteHeader(http.StatusNoContent)
}
case drivers.InvalidUploadID:
{
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
//objectResourcesMetadata := getObjectResources(req.URL.Query())
log.Println(bucket, object)
}
// List object parts
@ -386,28 +221,7 @@ func (server *minioAPI) listObjectPartsHandler(w http.ResponseWriter, req *http.
vars := mux.Vars(req)
bucket := vars["bucket"]
object := vars["object"]
objectResourcesMetadata, err := server.driver.ListObjectParts(bucket, object, objectResourcesMetadata)
switch iodine.ToError(err).(type) {
case nil:
{
response := generateListPartsResult(objectResourcesMetadata)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
case drivers.InvalidUploadID:
{
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
log.Println(bucket, object)
}
// Complete multipart upload
@ -434,34 +248,15 @@ func (server *minioAPI) completeMultipartUploadHandler(w http.ResponseWriter, re
vars := mux.Vars(req)
bucket := vars["bucket"]
object := vars["object"]
objectResourcesMetadata := getObjectResources(req.URL.Query())
log.Println(bucket, object)
//objectResourcesMetadata := getObjectResources(req.URL.Query())
partMap := make(map[int]string)
for _, part := range parts.Part {
partMap[part.PartNumber] = part.ETag
}
etag, err := server.driver.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, partMap)
switch iodine.ToError(err).(type) {
case nil:
{
response := generateCompleteMultpartUploadResult(bucket, object, "", etag)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
case drivers.InvalidUploadID:
{
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
/// Delete API

@ -14,7 +14,7 @@
* limitations under the License.
*/
package quota
package api
import "net/http"
@ -24,26 +24,26 @@ type rateLimit struct {
rateQueue chan bool
}
func (c *rateLimit) Add() {
func (c rateLimit) Add() {
c.rateQueue <- true // fill in the queue
return
}
func (c *rateLimit) Remove() {
func (c rateLimit) Remove() {
<-c.rateQueue // invalidate the queue, after the request is served
return
}
// ServeHTTP is an http.Handler ServeHTTP method
func (c *rateLimit) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (c rateLimit) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c.Add() // add
c.handler.ServeHTTP(w, req) // serve
c.Remove() // remove
}
// RateLimit limits the number of concurrent http requests
func RateLimit(handle http.Handler, limit int) http.Handler {
return &rateLimit{
// 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),
}

@ -16,42 +16,15 @@
package api
import (
"net/http"
import router "github.com/gorilla/mux"
router "github.com/gorilla/mux"
"github.com/minio/minio/pkg/api/logging"
"github.com/minio/minio/pkg/api/quota"
"github.com/minio/minio/pkg/storage/drivers"
)
type minioAPI struct{}
type minioAPI struct {
driver drivers.Driver
}
// Config api configurable parameters
type Config struct {
RateLimit int
driver drivers.Driver
}
// GetDriver - get a an existing set driver
func (c Config) GetDriver() drivers.Driver {
return c.driver
}
// SetDriver - set a new driver
func (c *Config) SetDriver(driver drivers.Driver) {
c.driver = driver
}
// HTTPHandler - http wrapper handler
func HTTPHandler(config Config) http.Handler {
var mux *router.Router
// Handler - api wrapper handler
func New(config Config) API {
var api = minioAPI{}
api.driver = config.GetDriver()
mux = router.NewRouter()
mux := router.NewRouter()
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
@ -75,12 +48,7 @@ func HTTPHandler(config Config) http.Handler {
handler = timeValidityHandler(handler)
handler = ignoreResourcesHandler(handler)
handler = validateAuthHeaderHandler(handler)
// handler = quota.BandwidthCap(h, 25*1024*1024, time.Duration(30*time.Minute))
// handler = quota.BandwidthCap(h, 100*1024*1024, time.Duration(24*time.Hour))
// handler = quota.RequestLimit(h, 100, time.Duration(30*time.Minute))
// handler = quota.RequestLimit(h, 1000, time.Duration(24*time.Hour))
// handler = quota.ConnectionLimit(handler, config.ConnectionLimit)
handler = quota.RateLimit(handler, config.RateLimit)
handler = logging.LogHandler(handler)
return handler
handler = rateLimitHandler(handler, config.RateLimit)
handler = loggingHandler(handler)
return API{config, handler}
}

@ -1,320 +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 api
import (
"net/http"
"github.com/gorilla/mux"
"github.com/minio/minio/pkg/iodine"
"github.com/minio/minio/pkg/storage/drivers"
"github.com/minio/minio/pkg/utils/log"
)
func (server *minioAPI) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool {
vars := mux.Vars(req)
bucket := vars["bucket"]
bucketMetadata, err := server.driver.GetBucketMetadata(bucket)
switch iodine.ToError(err).(type) {
case drivers.BucketNotFound:
{
writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
return false
}
case drivers.BucketNameInvalid:
{
writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
return false
}
case nil:
if _, err := stripAuth(req); err != nil {
if bucketMetadata.ACL.IsPrivate() {
return true
//uncomment this when we have webcli
//writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
//return false
}
if bucketMetadata.ACL.IsPublicRead() && req.Method == "PUT" {
return true
//uncomment this when we have webcli
//writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
//return false
}
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
return true
}
// 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) {
acceptsContentType := getContentType(req)
resources := getBucketMultipartResources(req.URL.Query())
if resources.MaxUploads == 0 {
resources.MaxUploads = maxObjectList
}
vars := mux.Vars(req)
bucket := vars["bucket"]
resources, err := server.driver.ListMultipartUploads(bucket, resources)
switch iodine.ToError(err).(type) {
case nil: // success
{
// generate response
response := generateListMultipartUploadsResult(bucket, resources)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
case drivers.BucketNotFound:
{
writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// 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) {
acceptsContentType := getContentType(req)
// verify if bucket allows this operation
if !server.isValidOp(w, req, acceptsContentType) {
return
}
if isRequestUploads(req.URL.Query()) {
server.listMultipartUploadsHandler(w, req)
return
}
resources := getBucketResources(req.URL.Query())
if resources.Maxkeys == 0 {
resources.Maxkeys = maxObjectList
}
vars := mux.Vars(req)
bucket := vars["bucket"]
objects, resources, err := server.driver.ListObjects(bucket, resources)
switch iodine.ToError(err).(type) {
case nil: // success
{
// generate response
response := generateListObjectsResponse(bucket, objects, resources)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
case drivers.ObjectNotFound:
{
writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
}
case drivers.ObjectNameInvalid:
{
writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// 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) {
acceptsContentType := getContentType(req)
// uncomment this when we have webcli
// without access key credentials one cannot list buckets
// if _, err := stripAuth(req); err != nil {
// writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
// return
// }
buckets, err := server.driver.ListBuckets()
switch iodine.ToError(err).(type) {
case nil:
{
// generate response
response := generateListBucketsResponse(buckets)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
// write response
w.Write(encodedSuccessResponse)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// 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) {
acceptsContentType := getContentType(req)
// uncomment this when we have webcli
// without access key credentials one cannot create a bucket
// if _, err := stripAuth(req); err != nil {
// writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
// return
// }
if isRequestBucketACL(req.URL.Query()) {
server.putBucketACLHandler(w, req)
return
}
// read from 'x-amz-acl'
aclType := getACLType(req)
if aclType == unsupportedACLType {
writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
return
}
vars := mux.Vars(req)
bucket := vars["bucket"]
err := server.driver.CreateBucket(bucket, getACLTypeString(aclType))
switch iodine.ToError(err).(type) {
case nil:
{
// Make sure to add Location information here only for bucket
w.Header().Set("Location", "/"+bucket)
writeSuccessResponse(w, acceptsContentType)
}
case drivers.TooManyBuckets:
{
writeErrorResponse(w, req, TooManyBuckets, acceptsContentType, req.URL.Path)
}
case drivers.BucketNameInvalid:
{
writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
}
case drivers.BucketExists:
{
writeErrorResponse(w, req, BucketAlreadyExists, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// 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) {
acceptsContentType := getContentType(req)
// read from 'x-amz-acl'
aclType := getACLType(req)
if aclType == unsupportedACLType {
writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
return
}
vars := mux.Vars(req)
bucket := vars["bucket"]
err := server.driver.SetBucketMetadata(bucket, getACLTypeString(aclType))
switch iodine.ToError(err).(type) {
case nil:
{
writeSuccessResponse(w, acceptsContentType)
}
case drivers.BucketNameInvalid:
{
writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
}
case drivers.BucketNotFound:
{
writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}
// 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) {
acceptsContentType := getContentType(req)
vars := mux.Vars(req)
bucket := vars["bucket"]
_, err := server.driver.GetBucketMetadata(bucket)
switch iodine.ToError(err).(type) {
case nil:
{
writeSuccessResponse(w, acceptsContentType)
}
case drivers.BucketNotFound:
{
error := getErrorCode(NoSuchBucket)
w.WriteHeader(error.HTTPStatusCode)
}
case drivers.BucketNameInvalid:
{
error := getErrorCode(InvalidBucketName)
w.WriteHeader(error.HTTPStatusCode)
}
default:
{
log.Error.Println(iodine.New(err, nil))
error := getErrorCode(InternalError)
w.WriteHeader(error.HTTPStatusCode)
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,152 +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 quota
import (
"errors"
"io"
"net"
"net/http"
"time"
"sync"
"github.com/minio/minio/pkg/iodine"
"github.com/minio/minio/pkg/utils/log"
)
// bandwidthQuotaHandler
type bandwidthQuotaHandler struct {
handler http.Handler
quotas *quotaMap
}
// ServeHTTP is an http.Handler ServeHTTP method
func (h *bandwidthQuotaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
host, _, _ := net.SplitHostPort(req.RemoteAddr)
longIP := longIP{net.ParseIP(host)}.IptoUint32()
if h.quotas.WillExceedQuota(longIP, req.ContentLength) {
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
log.Debug.Printf("Offending Host: %s, BandwidthUsed: %d", hosts, h.quotas.GetQuotaUsed(longIP))
writeErrorResponse(w, req, BandWidthInsufficientToProceed, req.URL.Path)
return
}
qr := &quotaReader{
ReadCloser: req.Body,
quotas: h.quotas,
ip: longIP,
w: w,
req: req,
lock: &sync.RWMutex{},
}
req.Body = qr
w = &quotaWriter{
ResponseWriter: w,
quotas: h.quotas,
ip: longIP,
quotaReader: qr,
}
h.handler.ServeHTTP(w, req)
}
// BandwidthCap sets a quote based upon bandwidth used
func BandwidthCap(h http.Handler, limit int64, duration time.Duration) http.Handler {
return &bandwidthQuotaHandler{
handler: h,
quotas: &quotaMap{
data: make(map[int64]map[uint32]int64),
limit: int64(limit),
duration: duration,
segmentSize: segmentSize(duration),
},
}
}
type quotaReader struct {
io.ReadCloser
quotas *quotaMap
ip uint32
w http.ResponseWriter
req *http.Request
err bool
lock *sync.RWMutex
}
func (q *quotaReader) Read(b []byte) (int, error) {
log.Println(q.quotas.GetQuotaUsed(q.ip))
log.Println(q.quotas.limit)
q.lock.Lock()
defer q.lock.Unlock()
if q.err {
return 0, iodine.New(errors.New("Quota Met"), nil)
}
if q.err == false && q.quotas.IsQuotaMet(q.ip) {
defer q.lock.Unlock()
q.err = true
hosts, _ := net.LookupAddr(uint32ToIP(q.ip).String())
log.Debug.Printf("Offending Host: %s, BandwidthUsed: %d", hosts, q.quotas.GetQuotaUsed(q.ip))
writeErrorResponse(q.w, q.req, BandWidthQuotaExceeded, q.req.URL.Path)
return 0, iodine.New(errors.New("Quota Met"), nil)
}
n, err := q.ReadCloser.Read(b)
q.quotas.Add(q.ip, int64(n))
return n, iodine.New(err, nil)
}
func (q *quotaReader) Close() error {
return iodine.New(q.ReadCloser.Close(), nil)
}
type quotaWriter struct {
ResponseWriter http.ResponseWriter
quotas *quotaMap
ip uint32
quotaReader *quotaReader
}
func (q *quotaWriter) Write(b []byte) (int, error) {
q.quotaReader.lock.RLock()
defer q.quotaReader.lock.RUnlock()
if q.quotas.IsQuotaMet(q.ip) {
return 0, iodine.New(errors.New("Quota Met"), nil)
}
q.quotas.Add(q.ip, int64(len(b)))
n, err := q.ResponseWriter.Write(b)
// remove from quota if a full write isn't performed
q.quotas.Add(q.ip, int64(n-len(b)))
return n, iodine.New(err, nil)
}
func (q *quotaWriter) Header() http.Header {
return q.ResponseWriter.Header()
}
func (q *quotaWriter) WriteHeader(status int) {
q.quotaReader.lock.RLock()
defer q.quotaReader.lock.RUnlock()
if q.quotas.IsQuotaMet(q.ip) || q.quotaReader.err {
return
}
q.ResponseWriter.WriteHeader(status)
}
func segmentSize(duration time.Duration) time.Duration {
var segmentSize time.Duration
for i := int64(1); i < duration.Nanoseconds(); i = i * 10 {
segmentSize = time.Duration(i)
}
return segmentSize
}

@ -1,89 +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 quota
import (
"net"
"net/http"
"sync"
"github.com/minio/minio/pkg/utils/log"
)
// requestLimitHandler
type connLimit struct {
sync.RWMutex
handler http.Handler
connections map[uint32]int
limit int
}
func (c *connLimit) IsLimitExceeded(ip uint32) bool {
if c.connections[ip] >= c.limit {
return true
}
return false
}
func (c *connLimit) GetUsed(ip uint32) int {
return c.connections[ip]
}
func (c *connLimit) Add(ip uint32) {
c.Lock()
defer c.Unlock()
count := c.connections[ip]
count = count + 1
c.connections[ip] = count
return
}
func (c *connLimit) Remove(ip uint32) {
c.Lock()
defer c.Unlock()
count, _ := c.connections[ip]
count = count - 1
if count <= 0 {
delete(c.connections, ip)
return
}
c.connections[ip] = count
}
// ServeHTTP is an http.Handler ServeHTTP method
func (c *connLimit) ServeHTTP(w http.ResponseWriter, req *http.Request) {
host, _, _ := net.SplitHostPort(req.RemoteAddr)
longIP := longIP{net.ParseIP(host)}.IptoUint32()
if c.IsLimitExceeded(longIP) {
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
log.Debug.Printf("Connection limit reached - Host: %s, Total Connections: %d\n", hosts, c.GetUsed(longIP))
writeErrorResponse(w, req, ConnectionLimitExceeded, req.URL.Path)
return
}
c.Add(longIP)
defer c.Remove(longIP)
c.handler.ServeHTTP(w, req)
}
// ConnectionLimit limits the number of concurrent connections
func ConnectionLimit(h http.Handler, limit int) http.Handler {
return &connLimit{
handler: h,
connections: make(map[uint32]int),
limit: limit,
}
}

@ -1,127 +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 quota
import (
"bytes"
"encoding/xml"
"net/http"
)
// copied from api, no cyclic deps allowed
// Error structure
type Error struct {
Code string
Description string
HTTPStatusCode int
}
// ErrorResponse - error response format
type ErrorResponse struct {
XMLName xml.Name `xml:"Error" json:"-"`
Code string
Message string
Resource string
RequestID string
HostID string
}
// Quota standard errors non exhaustive list
const (
RequestTimeTooSkewed = iota
BandWidthQuotaExceeded
BandWidthInsufficientToProceed
ConnectionLimitExceeded
SlowDown
)
// Golang http doesn't implement these
const (
StatusTooManyRequests = 429
)
func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorType int, resource string) {
error := getErrorCode(errorType)
errorResponse := getErrorResponse(error, resource)
encodedErrorResponse := encodeErrorResponse(errorResponse)
// set headers
writeErrorHeaders(w)
w.WriteHeader(error.HTTPStatusCode)
// write body
w.Write(encodedErrorResponse)
}
func writeErrorHeaders(w http.ResponseWriter) {
w.Header().Set("Server", "Minio")
w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("Content-Type", "application/xml")
w.Header().Set("Connection", "close")
}
// Error code to Error structure map
var errorCodeResponse = map[int]Error{
BandWidthQuotaExceeded: {
Code: "BandwidthQuotaExceeded",
Description: "Bandwidth Quota Exceeded.",
HTTPStatusCode: StatusTooManyRequests,
},
BandWidthInsufficientToProceed: {
Code: "BandwidthQuotaWillBeExceeded",
Description: "Bandwidth quota will be exceeded with this request.",
HTTPStatusCode: StatusTooManyRequests,
},
ConnectionLimitExceeded: {
Code: "ConnectionLimitExceeded",
Description: "Connections Limit Exceeded.",
HTTPStatusCode: StatusTooManyRequests,
},
SlowDown: {
Code: "SlowDown",
Description: "Reduce your request rate.",
HTTPStatusCode: StatusTooManyRequests,
},
}
// Write error response headers
func encodeErrorResponse(response interface{}) []byte {
var bytesBuffer bytes.Buffer
encoder := xml.NewEncoder(&bytesBuffer)
encoder.Encode(response)
return bytesBuffer.Bytes()
}
// errorCodeError provides errorCode to Error. It returns empty if the code provided is unknown
func getErrorCode(code int) Error {
return errorCodeResponse[code]
}
// getErrorResponse gets in standard error and resource value and
// provides a encodable populated response values
func getErrorResponse(err Error, resource string) ErrorResponse {
var data = ErrorResponse{}
data.Code = err.Code
data.Message = err.Description
if resource != "" {
data.Resource = resource
}
// TODO implement this in future
data.RequestID = "3L137"
data.HostID = "3L137"
return data
}

@ -1,96 +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 quota
import (
"encoding/binary"
"net"
"sync"
"time"
)
// map[minute][address] = current quota
type quotaMap struct {
sync.RWMutex
data map[int64]map[uint32]int64
limit int64
duration time.Duration
segmentSize time.Duration
}
func (q *quotaMap) CanExpire() {
q.Lock()
defer q.Unlock()
currentMinute := time.Now().UTC().UnixNano() / q.segmentSize.Nanoseconds()
// divide by segmentSize, otherwise expiredQuotas will always be negative
expiredQuotas := currentMinute - (q.duration.Nanoseconds() / q.segmentSize.Nanoseconds())
for time := range q.data {
if time < expiredQuotas {
delete(q.data, time)
}
}
}
func (q *quotaMap) Add(ip uint32, size int64) {
q.CanExpire()
q.Lock()
defer q.Unlock()
currentMinute := time.Now().UTC().UnixNano() / q.segmentSize.Nanoseconds()
if _, ok := q.data[currentMinute]; !ok {
q.data[currentMinute] = make(map[uint32]int64)
}
currentData, _ := q.data[currentMinute][ip]
proposedDataSize := currentData + size
q.data[currentMinute][ip] = proposedDataSize
}
func (q *quotaMap) IsQuotaMet(ip uint32) bool {
q.CanExpire()
if q.GetQuotaUsed(ip) >= q.limit {
return true
}
return false
}
func (q *quotaMap) GetQuotaUsed(ip uint32) (total int64) {
q.CanExpire()
q.RLock()
defer q.RUnlock()
for _, segment := range q.data {
if used, ok := segment[ip]; ok {
total += used
}
}
return
}
func (q *quotaMap) WillExceedQuota(ip uint32, size int64) (result bool) {
return q.GetQuotaUsed(ip)+size > q.limit
}
type longIP struct {
net.IP
}
// []byte to uint32 representation
func (p longIP) IptoUint32() (result uint32) {
ip := p.To4()
if ip == nil {
return 0
}
return binary.BigEndian.Uint32(ip)
}

@ -1,65 +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 quota
import (
"encoding/binary"
"net"
"net/http"
"time"
"github.com/minio/minio/pkg/utils/log"
)
// requestLimitHandler
type requestLimitHandler struct {
handler http.Handler
quotas *quotaMap
}
//convert a uint32 to an ipv4
func uint32ToIP(ip uint32) net.IP {
addr := net.IP{0, 0, 0, 0}
binary.BigEndian.PutUint32(addr, ip)
return addr
}
// ServeHTTP is an http.Handler ServeHTTP method
func (h *requestLimitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
host, _, _ := net.SplitHostPort(req.RemoteAddr)
longIP := longIP{net.ParseIP(host)}.IptoUint32()
if h.quotas.IsQuotaMet(longIP) {
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
log.Debug.Printf("Offending Host: %s, RequestUSED: %d\n", hosts, h.quotas.GetQuotaUsed(longIP))
writeErrorResponse(w, req, SlowDown, req.URL.Path)
}
h.quotas.Add(longIP, 1)
h.handler.ServeHTTP(w, req)
}
// RequestLimit sets a quote based upon number of requests allowed over a time period
func RequestLimit(h http.Handler, limit int64, duration time.Duration) http.Handler {
return &requestLimitHandler{
handler: h,
quotas: &quotaMap{
data: make(map[int64]map[uint32]int64),
limit: int64(limit),
duration: duration,
segmentSize: segmentSize(duration),
},
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package httpserver
package api
import (
"fmt"
@ -32,38 +32,27 @@ type Config struct {
RateLimit int
}
// Server - http server related
type Server struct {
config Config
handler http.Handler
}
// Start http server
func Start(handler http.Handler, config Config) (chan<- string, <-chan error, Server) {
ctrlChannel := make(chan string)
errorChannel := make(chan error)
server := Server{
config: config,
handler: handler,
}
go start(ctrlChannel, errorChannel, server)
return ctrlChannel, errorChannel, server
func Start(a API) <-chan error {
errCh := make(chan error)
go start(errCh, a)
return errCh
}
func start(ctrlChannel <-chan string, errorChannel chan<- error, server Server) {
defer close(errorChannel)
func start(errCh chan error, a API) {
defer close(errCh)
var err error
// Minio server config
httpServer := &http.Server{
Addr: server.config.Address,
Handler: server.handler,
Addr: a.config.Address,
Handler: a.handler,
MaxHeaderBytes: 1 << 20,
}
host, port, err := net.SplitHostPort(server.config.Address)
host, port, err := net.SplitHostPort(a.config.Address)
if err != nil {
errorChannel <- err
errCh <- err
return
}
@ -74,7 +63,7 @@ func start(ctrlChannel <-chan string, errorChannel chan<- error, server Server)
default:
addrs, err := net.InterfaceAddrs()
if err != nil {
errorChannel <- err
errCh <- err
return
}
for _, addr := range addrs {
@ -92,14 +81,31 @@ func start(ctrlChannel <-chan string, errorChannel chan<- error, server Server)
fmt.Printf("Starting minio server on: http://%s:%s\n", host, port)
}
err = httpServer.ListenAndServe()
case server.config.TLS == true:
case a.config.TLS == true:
for _, host := range hosts {
fmt.Printf("Starting minio server on: https://%s:%s\n", host, port)
}
err = httpServer.ListenAndServeTLS(server.config.CertFile, server.config.KeyFile)
err = httpServer.ListenAndServeTLS(a.config.CertFile, a.config.KeyFile)
}
if err != nil {
errorChannel <- err
errCh <- err
}
errCh <- nil
return
}
// API is used to build api server
type API struct {
config Config
handler http.Handler
}
// StartServer APIFactory builds api server
func StartServer(conf Config) error {
for err := range Start(New(conf)) {
if err != nil {
return err
}
}
return nil
}

@ -1,129 +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 server
import (
"errors"
"fmt"
"reflect"
"time"
"github.com/minio/minio/pkg/api"
"github.com/minio/minio/pkg/api/web"
"github.com/minio/minio/pkg/iodine"
"github.com/minio/minio/pkg/server/httpserver"
"github.com/minio/minio/pkg/storage/drivers"
"github.com/minio/minio/pkg/storage/drivers/cache"
"github.com/minio/minio/pkg/storage/drivers/donut"
"github.com/minio/minio/pkg/utils/log"
)
// WebFactory is used to build web cli server
type WebFactory struct {
httpserver.Config
}
// GetStartServerFunc builds web cli server
func (f WebFactory) GetStartServerFunc() StartServerFunc {
return func() (chan<- string, <-chan error) {
ctrl, status, _ := httpserver.Start(web.HTTPHandler(), f.Config)
return ctrl, status
}
}
// Factory is used to build api server
type Factory struct {
httpserver.Config
Paths []string
MaxMemory uint64
Expiration time.Duration
}
// GetStartServerFunc Factory builds api server
func (f Factory) GetStartServerFunc() StartServerFunc {
return func() (chan<- string, <-chan error) {
conf := api.Config{RateLimit: f.RateLimit}
var driver drivers.Driver
var err error
if len(f.Paths) != 0 {
driver, err = donut.NewDriver(f.Paths)
if err != nil {
log.Fatalln(err)
}
driver, err = cache.NewDriver(f.MaxMemory, f.Expiration, driver)
if err != nil {
log.Fatalln(err)
}
}
conf.SetDriver(driver)
ctrl, status, _ := httpserver.Start(api.HTTPHandler(conf), f.Config)
return ctrl, status
}
}
// StartServerFunc describes a function that can be used to start a server with StartMinio
type StartServerFunc func() (chan<- string, <-chan error)
// StartMinio starts minio server
func StartMinio(servers []StartServerFunc) {
var ctrlChannels []chan<- string
var errChannels []<-chan error
for _, server := range servers {
ctrlChannel, errChannel := server()
ctrlChannels = append(ctrlChannels, ctrlChannel)
errChannels = append(errChannels, errChannel)
}
cases := createSelectCases(errChannels)
for len(cases) > 0 {
chosen, value, recvOk := reflect.Select(cases)
switch recvOk {
case true:
// Status Message Received
switch true {
case value.Interface() != nil:
// For any error received cleanup all existing channels and fail
for _, ch := range ctrlChannels {
close(ch)
}
msg := fmt.Sprintf("%q", value.Interface())
log.Fatal(iodine.New(errors.New(msg), nil))
}
case false:
// Channel closed, remove from list
var aliveStatusChans []<-chan error
for i, ch := range errChannels {
if i != chosen {
aliveStatusChans = append(aliveStatusChans, ch)
}
}
// create new select cases without defunct channel
errChannels = aliveStatusChans
cases = createSelectCases(errChannels)
}
}
}
func createSelectCases(channels []<-chan error) []reflect.SelectCase {
cases := make([]reflect.SelectCase, len(channels))
for i, ch := range channels {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
}
}
return cases
}
Loading…
Cancel
Save