storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/http/server.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2017, 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package http 18 19 import ( 20 "crypto/tls" 21 "errors" 22 "io/ioutil" 23 "net/http" 24 "runtime/pprof" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 humanize "github.com/dustin/go-humanize" 30 "github.com/minio/minio-go/v7/pkg/set" 31 32 "storj.io/minio/cmd/config" 33 "storj.io/minio/cmd/config/api" 34 "storj.io/minio/pkg/certs" 35 "storj.io/minio/pkg/env" 36 "storj.io/minio/pkg/fips" 37 ) 38 39 const ( 40 serverShutdownPoll = 500 * time.Millisecond 41 42 // DefaultShutdownTimeout - default shutdown timeout used for graceful http server shutdown. 43 DefaultShutdownTimeout = 5 * time.Second 44 45 // DefaultMaxHeaderBytes - default maximum HTTP header size in bytes. 46 DefaultMaxHeaderBytes = 1 * humanize.MiByte 47 ) 48 49 // Server - extended http.Server supports multiple addresses to serve and enhanced connection handling. 50 type Server struct { 51 http.Server 52 Addrs []string // addresses on which the server listens for new connection. 53 ShutdownTimeout time.Duration // timeout used for graceful server shutdown. 54 listenerMutex sync.Mutex // to guard 'listener' field. 55 listener *httpListener // HTTP listener for all 'Addrs' field. 56 inShutdown uint32 // indicates whether the server is in shutdown or not 57 requestCount int32 // counter holds no. of request in progress. 58 } 59 60 // GetRequestCount - returns number of request in progress. 61 func (srv *Server) GetRequestCount() int { 62 return int(atomic.LoadInt32(&srv.requestCount)) 63 } 64 65 // Start - start HTTP server 66 func (srv *Server) Start() (err error) { 67 // Take a copy of server fields. 68 var tlsConfig *tls.Config 69 if srv.TLSConfig != nil { 70 tlsConfig = srv.TLSConfig.Clone() 71 } 72 handler := srv.Handler // if srv.Handler holds non-synced state -> possible data race 73 74 addrs := set.CreateStringSet(srv.Addrs...).ToSlice() // copy and remove duplicates 75 76 // Create new HTTP listener. 77 var listener *httpListener 78 listener, err = newHTTPListener( 79 addrs, 80 ) 81 if err != nil { 82 return err 83 } 84 85 // Wrap given handler to do additional 86 // * return 503 (service unavailable) if the server in shutdown. 87 wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 88 // If server is in shutdown. 89 if atomic.LoadUint32(&srv.inShutdown) != 0 { 90 // To indicate disable keep-alives 91 w.Header().Set("Connection", "close") 92 w.WriteHeader(http.StatusForbidden) 93 w.Write([]byte(http.ErrServerClosed.Error())) 94 w.(http.Flusher).Flush() 95 return 96 } 97 98 atomic.AddInt32(&srv.requestCount, 1) 99 defer atomic.AddInt32(&srv.requestCount, -1) 100 101 // Handle request using passed handler. 102 handler.ServeHTTP(w, r) 103 }) 104 105 srv.listenerMutex.Lock() 106 srv.Handler = wrappedHandler 107 srv.listener = listener 108 srv.listenerMutex.Unlock() 109 110 // Start servicing with listener. 111 if tlsConfig != nil { 112 return srv.Server.Serve(tls.NewListener(listener, tlsConfig)) 113 } 114 return srv.Server.Serve(listener) 115 } 116 117 // Shutdown - shuts down HTTP server. 118 func (srv *Server) Shutdown() error { 119 srv.listenerMutex.Lock() 120 if srv.listener == nil { 121 srv.listenerMutex.Unlock() 122 return http.ErrServerClosed 123 } 124 srv.listenerMutex.Unlock() 125 126 if atomic.AddUint32(&srv.inShutdown, 1) > 1 { 127 // shutdown in progress 128 return http.ErrServerClosed 129 } 130 131 // Close underneath HTTP listener. 132 srv.listenerMutex.Lock() 133 err := srv.listener.Close() 134 srv.listenerMutex.Unlock() 135 if err != nil { 136 return err 137 } 138 139 // Wait for opened connection to be closed up to Shutdown timeout. 140 shutdownTimeout := srv.ShutdownTimeout 141 shutdownTimer := time.NewTimer(shutdownTimeout) 142 ticker := time.NewTicker(serverShutdownPoll) 143 defer ticker.Stop() 144 for { 145 select { 146 case <-shutdownTimer.C: 147 // Write all running goroutines. 148 tmp, err := ioutil.TempFile("", "minio-goroutines-*.txt") 149 if err == nil { 150 _ = pprof.Lookup("goroutine").WriteTo(tmp, 1) 151 tmp.Close() 152 return errors.New("timed out. some connections are still active. goroutines written to " + tmp.Name()) 153 } 154 return errors.New("timed out. some connections are still active") 155 case <-ticker.C: 156 if atomic.LoadInt32(&srv.requestCount) <= 0 { 157 return nil 158 } 159 } 160 } 161 } 162 163 // NewServer - creates new HTTP server using given arguments. 164 func NewServer(addrs []string, handler http.Handler, getCert certs.GetCertificateFunc) *Server { 165 secureCiphers := env.Get(api.EnvAPISecureCiphers, config.EnableOn) == config.EnableOn 166 167 var tlsConfig *tls.Config 168 if getCert != nil { 169 tlsConfig = &tls.Config{ 170 PreferServerCipherSuites: true, 171 MinVersion: tls.VersionTLS12, 172 NextProtos: []string{"http/1.1", "h2"}, 173 GetCertificate: getCert, 174 } 175 if secureCiphers || fips.Enabled() { 176 tlsConfig.CipherSuites = fips.CipherSuitesTLS() 177 tlsConfig.CurvePreferences = fips.EllipticCurvesTLS() 178 } 179 } 180 181 httpServer := &Server{ 182 Addrs: addrs, 183 ShutdownTimeout: DefaultShutdownTimeout, 184 } 185 httpServer.Handler = handler 186 httpServer.TLSConfig = tlsConfig 187 httpServer.MaxHeaderBytes = DefaultMaxHeaderBytes 188 189 return httpServer 190 }