github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/api/server/server.go (about) 1 package server 2 3 import ( 4 "context" 5 "log" 6 "net" 7 "net/http" 8 "os" 9 "os/signal" 10 "runtime" 11 "strings" 12 "sync" 13 "syscall" 14 "time" 15 16 "github.com/containers/libpod/libpod" 17 "github.com/containers/libpod/pkg/api/handlers" 18 "github.com/coreos/go-systemd/v22/activation" 19 "github.com/gorilla/mux" 20 "github.com/gorilla/schema" 21 "github.com/pkg/errors" 22 "github.com/sirupsen/logrus" 23 ) 24 25 type APIServer struct { 26 http.Server // The HTTP work happens here 27 *schema.Decoder // Decoder for Query parameters to structs 28 context.Context // Context to carry objects to handlers 29 *libpod.Runtime // Where the real work happens 30 net.Listener // mux for routing HTTP API calls to libpod routines 31 context.CancelFunc // Stop APIServer 32 idleTracker *IdleTracker // Track connections to support idle shutdown 33 } 34 35 // Number of seconds to wait for next request, if exceeded shutdown server 36 const ( 37 DefaultServiceDuration = 300 * time.Second 38 UnlimitedServiceDuration = 0 * time.Second 39 ) 40 41 // NewServer will create and configure a new API server with all defaults 42 func NewServer(runtime *libpod.Runtime) (*APIServer, error) { 43 return newServer(runtime, DefaultServiceDuration, nil) 44 } 45 46 // NewServerWithSettings will create and configure a new API server using provided settings 47 func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { 48 return newServer(runtime, duration, listener) 49 } 50 51 func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { 52 // If listener not provided try socket activation protocol 53 if listener == nil { 54 if _, found := os.LookupEnv("LISTEN_FDS"); !found { 55 return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.") 56 } 57 58 listeners, err := activation.Listeners() 59 if err != nil { 60 return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd") 61 } 62 if len(listeners) != 1 { 63 return nil, errors.Errorf("Wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners)) 64 } 65 listener = &listeners[0] 66 } 67 68 router := mux.NewRouter().UseEncodedPath() 69 idle := NewIdleTracker(duration) 70 71 server := APIServer{ 72 Server: http.Server{ 73 Handler: router, 74 ReadHeaderTimeout: 20 * time.Second, 75 IdleTimeout: duration, 76 ConnState: idle.ConnState, 77 ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), 78 }, 79 Decoder: handlers.NewAPIDecoder(), 80 idleTracker: idle, 81 Listener: *listener, 82 Runtime: runtime, 83 } 84 85 router.NotFoundHandler = http.HandlerFunc( 86 func(w http.ResponseWriter, r *http.Request) { 87 // We can track user errors... 88 logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusNotFound, http.StatusText(http.StatusNotFound), r.Method, r.URL.String()) 89 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) 90 }, 91 ) 92 93 for _, fn := range []func(*mux.Router) error{ 94 server.registerAuthHandlers, 95 server.registerContainersHandlers, 96 server.registerDistributionHandlers, 97 server.registerEventsHandlers, 98 server.registerExecHandlers, 99 server.registerHealthCheckHandlers, 100 server.registerImagesHandlers, 101 server.registerInfoHandlers, 102 server.registerManifestHandlers, 103 server.registerMonitorHandlers, 104 server.registerPingHandlers, 105 server.registerPluginsHandlers, 106 server.registerPodsHandlers, 107 server.RegisterSwaggerHandlers, 108 server.registerSwarmHandlers, 109 server.registerSystemHandlers, 110 server.registerVersionHandlers, 111 server.registerVolumeHandlers, 112 } { 113 if err := fn(router); err != nil { 114 return nil, err 115 } 116 } 117 118 if logrus.IsLevelEnabled(logrus.DebugLevel) { 119 router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint 120 path, err := route.GetPathTemplate() 121 if err != nil { 122 path = "<N/A>" 123 } 124 methods, err := route.GetMethods() 125 if err != nil { 126 methods = []string{"<N/A>"} 127 } 128 logrus.Debugf("Methods: %s Path: %s", strings.Join(methods, ", "), path) 129 return nil 130 }) 131 } 132 133 return &server, nil 134 } 135 136 // Serve starts responding to HTTP requests 137 func (s *APIServer) Serve() error { 138 sigChan := make(chan os.Signal, 1) 139 signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) 140 errChan := make(chan error, 1) 141 142 go func() { 143 <-s.idleTracker.Done() 144 logrus.Debugf("API Server idle for %v", s.idleTracker.Duration) 145 _ = s.Shutdown() 146 }() 147 148 go func() { 149 err := s.Server.Serve(s.Listener) 150 if err != nil && err != http.ErrServerClosed { 151 errChan <- errors.Wrap(err, "failed to start API server") 152 return 153 } 154 errChan <- nil 155 }() 156 157 select { 158 case err := <-errChan: 159 return err 160 case sig := <-sigChan: 161 logrus.Infof("APIServer terminated by signal %v", sig) 162 } 163 164 return nil 165 } 166 167 // Shutdown is a clean shutdown waiting on existing clients 168 func (s *APIServer) Shutdown() error { 169 if logrus.IsLevelEnabled(logrus.DebugLevel) { 170 _, file, line, _ := runtime.Caller(1) 171 logrus.Debugf("APIServer.Shutdown by %s:%d, %d/%d connection(s)", 172 file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) 173 } 174 175 // Duration == 0 flags no auto-shutdown of the server 176 if s.idleTracker.Duration == 0 { 177 logrus.Debug("APIServer.Shutdown ignored as Duration == 0") 178 return nil 179 } 180 181 // Gracefully shutdown server, duration of wait same as idle window 182 ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) 183 defer cancel() 184 go func() { 185 err := s.Server.Shutdown(ctx) 186 if err != nil && err != context.Canceled && err != http.ErrServerClosed { 187 logrus.Errorf("Failed to cleanly shutdown APIServer: %s", err.Error()) 188 } 189 }() 190 <-ctx.Done() 191 return nil 192 } 193 194 // Close immediately stops responding to clients and exits 195 func (s *APIServer) Close() error { 196 return s.Server.Close() 197 } 198 199 type IdleTracker struct { 200 active map[net.Conn]struct{} 201 total int 202 mux sync.Mutex 203 timer *time.Timer 204 Duration time.Duration 205 } 206 207 func NewIdleTracker(idle time.Duration) *IdleTracker { 208 return &IdleTracker{ 209 active: make(map[net.Conn]struct{}), 210 Duration: idle, 211 timer: time.NewTimer(idle), 212 } 213 } 214 215 func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) { 216 t.mux.Lock() 217 defer t.mux.Unlock() 218 219 oldActive := len(t.active) 220 logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, t.ActiveConnections(), t.TotalConnections()) 221 switch state { 222 case http.StateNew, http.StateActive, http.StateHijacked: 223 t.active[conn] = struct{}{} 224 // stop the timer if we transitioned from idle 225 if oldActive == 0 { 226 t.timer.Stop() 227 } 228 t.total += 1 229 case http.StateIdle, http.StateClosed: 230 delete(t.active, conn) 231 // Restart the timer if we've become idle 232 if oldActive > 0 && len(t.active) == 0 { 233 t.timer.Stop() 234 t.timer.Reset(t.Duration) 235 } 236 } 237 } 238 239 func (t *IdleTracker) ActiveConnections() int { 240 return len(t.active) 241 } 242 243 func (t *IdleTracker) TotalConnections() int { 244 return t.total 245 } 246 247 func (t *IdleTracker) Done() <-chan time.Time { 248 return t.timer.C 249 }