2017-07-12 19:33:21 -04:00
|
|
|
/*
|
2019-04-09 14:39:42 -04:00
|
|
|
* MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc.
|
2017-07-12 19:33:21 -04:00
|
|
|
*
|
|
|
|
* 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 http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"errors"
|
2019-11-29 09:10:08 -05:00
|
|
|
"io/ioutil"
|
2017-07-12 19:33:21 -04:00
|
|
|
"net/http"
|
2019-11-29 09:10:08 -05:00
|
|
|
"runtime/pprof"
|
2017-07-12 19:33:21 -04:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
humanize "github.com/dustin/go-humanize"
|
2019-03-14 14:57:35 -04:00
|
|
|
|
2020-07-14 12:38:05 -04:00
|
|
|
"github.com/minio/minio-go/v7/pkg/set"
|
2020-07-13 17:20:21 -04:00
|
|
|
"github.com/minio/minio/cmd/config"
|
2020-11-02 20:21:56 -05:00
|
|
|
"github.com/minio/minio/cmd/config/api"
|
2018-05-31 15:30:15 -04:00
|
|
|
"github.com/minio/minio/pkg/certs"
|
2020-07-13 17:20:21 -04:00
|
|
|
"github.com/minio/minio/pkg/env"
|
2021-04-14 11:29:56 -04:00
|
|
|
"github.com/minio/minio/pkg/fips"
|
2017-07-12 19:33:21 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
serverShutdownPoll = 500 * time.Millisecond
|
|
|
|
|
|
|
|
// DefaultShutdownTimeout - default shutdown timeout used for graceful http server shutdown.
|
|
|
|
DefaultShutdownTimeout = 5 * time.Second
|
|
|
|
|
|
|
|
// DefaultMaxHeaderBytes - default maximum HTTP header size in bytes.
|
|
|
|
DefaultMaxHeaderBytes = 1 * humanize.MiByte
|
|
|
|
)
|
|
|
|
|
|
|
|
// Server - extended http.Server supports multiple addresses to serve and enhanced connection handling.
|
|
|
|
type Server struct {
|
|
|
|
http.Server
|
2020-07-03 13:03:41 -04:00
|
|
|
Addrs []string // addresses on which the server listens for new connection.
|
|
|
|
ShutdownTimeout time.Duration // timeout used for graceful server shutdown.
|
|
|
|
listenerMutex sync.Mutex // to guard 'listener' field.
|
|
|
|
listener *httpListener // HTTP listener for all 'Addrs' field.
|
|
|
|
inShutdown uint32 // indicates whether the server is in shutdown or not
|
|
|
|
requestCount int32 // counter holds no. of request in progress.
|
2018-06-27 21:59:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetRequestCount - returns number of request in progress.
|
2020-12-02 14:12:00 -05:00
|
|
|
func (srv *Server) GetRequestCount() int {
|
|
|
|
return int(atomic.LoadInt32(&srv.requestCount))
|
2017-07-12 19:33:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start - start HTTP server
|
|
|
|
func (srv *Server) Start() (err error) {
|
|
|
|
// Take a copy of server fields.
|
2018-01-12 22:42:11 -05:00
|
|
|
var tlsConfig *tls.Config
|
|
|
|
if srv.TLSConfig != nil {
|
|
|
|
tlsConfig = srv.TLSConfig.Clone()
|
|
|
|
}
|
|
|
|
handler := srv.Handler // if srv.Handler holds non-synced state -> possible data race
|
2017-07-12 19:33:21 -04:00
|
|
|
|
2018-01-12 22:42:11 -05:00
|
|
|
addrs := set.CreateStringSet(srv.Addrs...).ToSlice() // copy and remove duplicates
|
2017-07-12 19:33:21 -04:00
|
|
|
|
|
|
|
// Create new HTTP listener.
|
|
|
|
var listener *httpListener
|
|
|
|
listener, err = newHTTPListener(
|
|
|
|
addrs,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wrap given handler to do additional
|
|
|
|
// * return 503 (service unavailable) if the server in shutdown.
|
|
|
|
wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2020-01-17 08:48:39 -05:00
|
|
|
// If server is in shutdown.
|
2017-07-12 19:33:21 -04:00
|
|
|
if atomic.LoadUint32(&srv.inShutdown) != 0 {
|
2020-01-17 08:48:39 -05:00
|
|
|
// To indicate disable keep-alives
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
|
|
|
w.Write([]byte(http.ErrServerClosed.Error()))
|
|
|
|
w.(http.Flusher).Flush()
|
2017-07-12 19:33:21 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-17 08:48:39 -05:00
|
|
|
atomic.AddInt32(&srv.requestCount, 1)
|
|
|
|
defer atomic.AddInt32(&srv.requestCount, -1)
|
|
|
|
|
2017-07-12 19:33:21 -04:00
|
|
|
// Handle request using passed handler.
|
|
|
|
handler.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
|
|
|
|
srv.listenerMutex.Lock()
|
|
|
|
srv.Handler = wrappedHandler
|
|
|
|
srv.listener = listener
|
|
|
|
srv.listenerMutex.Unlock()
|
|
|
|
|
|
|
|
// Start servicing with listener.
|
2019-02-14 20:53:46 -05:00
|
|
|
if tlsConfig != nil {
|
|
|
|
return srv.Server.Serve(tls.NewListener(listener, tlsConfig))
|
|
|
|
}
|
2017-07-12 19:33:21 -04:00
|
|
|
return srv.Server.Serve(listener)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown - shuts down HTTP server.
|
|
|
|
func (srv *Server) Shutdown() error {
|
2018-01-12 22:42:11 -05:00
|
|
|
srv.listenerMutex.Lock()
|
2017-08-04 13:44:46 -04:00
|
|
|
if srv.listener == nil {
|
2018-01-12 22:42:11 -05:00
|
|
|
srv.listenerMutex.Unlock()
|
2020-01-17 08:48:39 -05:00
|
|
|
return http.ErrServerClosed
|
2017-08-04 13:44:46 -04:00
|
|
|
}
|
2018-01-12 22:42:11 -05:00
|
|
|
srv.listenerMutex.Unlock()
|
2017-08-04 13:44:46 -04:00
|
|
|
|
2017-07-12 19:33:21 -04:00
|
|
|
if atomic.AddUint32(&srv.inShutdown, 1) > 1 {
|
|
|
|
// shutdown in progress
|
2020-01-17 08:48:39 -05:00
|
|
|
return http.ErrServerClosed
|
2017-07-12 19:33:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close underneath HTTP listener.
|
|
|
|
srv.listenerMutex.Lock()
|
|
|
|
err := srv.listener.Close()
|
|
|
|
srv.listenerMutex.Unlock()
|
2020-09-28 22:39:32 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-12 19:33:21 -04:00
|
|
|
|
|
|
|
// Wait for opened connection to be closed up to Shutdown timeout.
|
|
|
|
shutdownTimeout := srv.ShutdownTimeout
|
|
|
|
shutdownTimer := time.NewTimer(shutdownTimeout)
|
|
|
|
ticker := time.NewTicker(serverShutdownPoll)
|
|
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-shutdownTimer.C:
|
2019-11-29 09:10:08 -05:00
|
|
|
// Write all running goroutines.
|
|
|
|
tmp, err := ioutil.TempFile("", "minio-goroutines-*.txt")
|
|
|
|
if err == nil {
|
|
|
|
_ = pprof.Lookup("goroutine").WriteTo(tmp, 1)
|
|
|
|
tmp.Close()
|
2020-09-28 22:39:32 -04:00
|
|
|
return errors.New("timed out. some connections are still active. goroutines written to " + tmp.Name())
|
2019-11-29 09:10:08 -05:00
|
|
|
}
|
2020-09-28 22:39:32 -04:00
|
|
|
return errors.New("timed out. some connections are still active")
|
2017-07-12 19:33:21 -04:00
|
|
|
case <-ticker.C:
|
|
|
|
if atomic.LoadInt32(&srv.requestCount) <= 0 {
|
2020-09-28 22:39:32 -04:00
|
|
|
return nil
|
2017-07-12 19:33:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewServer - creates new HTTP server using given arguments.
|
2018-05-31 15:30:15 -04:00
|
|
|
func NewServer(addrs []string, handler http.Handler, getCert certs.GetCertificateFunc) *Server {
|
2020-11-02 20:21:56 -05:00
|
|
|
secureCiphers := env.Get(api.EnvAPISecureCiphers, config.EnableOn) == config.EnableOn
|
2020-07-13 17:20:21 -04:00
|
|
|
|
2017-07-12 19:33:21 -04:00
|
|
|
var tlsConfig *tls.Config
|
2018-05-31 15:30:15 -04:00
|
|
|
if getCert != nil {
|
2017-07-12 19:33:21 -04:00
|
|
|
tlsConfig = &tls.Config{
|
|
|
|
PreferServerCipherSuites: true,
|
|
|
|
MinVersion: tls.VersionTLS12,
|
2021-02-11 18:53:04 -05:00
|
|
|
NextProtos: []string{"http/1.1", "h2"},
|
2021-04-14 11:29:56 -04:00
|
|
|
GetCertificate: getCert,
|
|
|
|
}
|
|
|
|
if secureCiphers || fips.Enabled() {
|
|
|
|
tlsConfig.CipherSuites = fips.CipherSuitesTLS()
|
|
|
|
tlsConfig.CurvePreferences = fips.EllipticCurvesTLS()
|
2017-07-12 19:33:21 -04:00
|
|
|
}
|
2020-07-13 17:20:21 -04:00
|
|
|
}
|
|
|
|
|
2017-07-12 19:33:21 -04:00
|
|
|
httpServer := &Server{
|
2020-07-03 13:03:41 -04:00
|
|
|
Addrs: addrs,
|
|
|
|
ShutdownTimeout: DefaultShutdownTimeout,
|
2017-07-12 19:33:21 -04:00
|
|
|
}
|
|
|
|
httpServer.Handler = handler
|
|
|
|
httpServer.TLSConfig = tlsConfig
|
|
|
|
httpServer.MaxHeaderBytes = DefaultMaxHeaderBytes
|
|
|
|
|
|
|
|
return httpServer
|
|
|
|
}
|