github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/api/v1/server.gotmpl (about) 1 // Code generated by go-swagger; DO NOT EDIT. 2 3 4 {{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }} 5 6 7 package {{ .APIPackage }} 8 9 import ( 10 "context" 11 "crypto/tls" 12 "errors" 13 "log" 14 "net" 15 "net/http" 16 "os" 17 "os/signal" 18 "strconv" 19 "sync" 20 "sync/atomic" 21 "syscall" 22 "time" 23 24 25 "github.com/go-openapi/swag" 26 {{ if .UseGoStructFlags }}flags "github.com/jessevdk/go-flags" 27 {{ end -}} 28 "github.com/go-openapi/runtime/flagext" 29 {{ if .UsePFlags }}flag "github.com/spf13/pflag" 30 {{ end -}} 31 "golang.org/x/net/netutil" 32 33 {{ range .DefaultImports }}{{ printf "%q" . }} 34 {{ end }} 35 {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 36 {{ end }} 37 ) 38 39 const ( 40 schemeHTTP = "http" 41 schemeHTTPS = "https" 42 schemeUnix = "unix" 43 ) 44 45 var defaultSchemes []string 46 47 func init() { 48 defaultSchemes = []string{ {{ if (hasInsecure .Schemes) }} 49 schemeHTTP,{{ end}}{{ if (hasSecure .Schemes) }} 50 schemeHTTPS,{{ end }}{{ if (contains .ExtraSchemes "unix") }} 51 schemeUnix,{{ end }} 52 } 53 } 54 55 {{ if .UsePFlags }} 56 var ({{ if .ExcludeSpec }} 57 specFile string 58 {{ end }}enabledListeners []string 59 cleanupTimeout time.Duration 60 gracefulTimeout time.Duration 61 maxHeaderSize flagext.ByteSize 62 63 socketPath string 64 65 host string 66 port int 67 listenLimit int 68 keepAlive time.Duration 69 readTimeout time.Duration 70 writeTimeout time.Duration 71 72 tlsHost string 73 tlsPort int 74 tlsListenLimit int 75 tlsKeepAlive time.Duration 76 tlsReadTimeout time.Duration 77 tlsWriteTimeout time.Duration 78 tlsCertificate string 79 tlsCertificateKey string 80 tlsCACertificate string 81 ) 82 83 func init() { 84 maxHeaderSize = flagext.ByteSize(1000000){{ if .ExcludeSpec }} 85 flag.StringVarP(&specFile, "spec", "", "", "the swagger specification to serve") 86 {{ end }} 87 88 flag.StringSliceVar(&enabledListeners, "scheme", defaultSchemes, "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec") 89 flag.DurationVar(&cleanupTimeout, "cleanup-timeout", 10*time.Second, "grace period for which to wait before killing idle connections") 90 flag.DurationVar(&gracefulTimeout, "graceful-timeout", 15*time.Second, "grace period for which to wait before shutting down the server") 91 flag.Var(&maxHeaderSize, "max-header-size", "controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body") 92 93 flag.StringVar(&socketPath, "socket-path", "/var/run/todo-list.sock", "the unix socket to listen on") 94 95 flag.StringVar(&host, "host", "localhost", "the IP to listen on") 96 flag.IntVar(&port, "port", 0, "the port to listen on for insecure connections, defaults to a random value") 97 flag.IntVar(&listenLimit, "listen-limit", 0, "limit the number of outstanding requests") 98 flag.DurationVar(&keepAlive, "keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)") 99 flag.DurationVar(&readTimeout, "read-timeout", 30*time.Second, "maximum duration before timing out read of the request") 100 flag.DurationVar(&writeTimeout, "write-timeout", 30*time.Second, "maximum duration before timing out write of the response") 101 102 flag.StringVar(&tlsHost, "tls-host", "localhost", "the IP to listen on") 103 flag.IntVar(&tlsPort, "tls-port", 0, "the port to listen on for secure connections, defaults to a random value") 104 flag.StringVar(&tlsCertificate, "tls-certificate", "", "the certificate file to use for secure connections") 105 flag.StringVar(&tlsCertificateKey, "tls-key", "", "the private key file to use for secure connections (without passphrase)") 106 flag.StringVar(&tlsCACertificate, "tls-ca", "", "the certificate authority certificate file to be used with mutual tls auth") 107 flag.IntVar(&tlsListenLimit, "tls-listen-limit", 0, "limit the number of outstanding requests") 108 flag.DurationVar(&tlsKeepAlive, "tls-keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)") 109 flag.DurationVar(&tlsReadTimeout, "tls-read-timeout", 30*time.Second, "maximum duration before timing out read of the request") 110 flag.DurationVar(&tlsWriteTimeout, "tls-write-timeout", 30*time.Second, "maximum duration before timing out write of the response") 111 } 112 113 func stringEnvOverride(orig string, def string, keys ...string) string { 114 for _, k := range keys { 115 if os.Getenv(k) != "" { 116 return os.Getenv(k) 117 } 118 } 119 if def != "" && orig == "" { 120 return def 121 } 122 return orig 123 } 124 125 func intEnvOverride(orig int, def int, keys ...string) int { 126 for _, k := range keys { 127 if os.Getenv(k) != "" { 128 v, err := strconv.Atoi(os.Getenv(k)) 129 if err != nil { 130 fmt.Fprintln(os.Stderr, k, "is not a valid number") 131 os.Exit(1) 132 } 133 return v 134 } 135 } 136 if def != 0 && orig == 0 { 137 return def 138 } 139 return orig 140 } 141 {{ end }} 142 143 // NewServer creates a new api {{ humanize .Name }} server but does not configure it 144 func NewServer(api *{{ .Package }}.{{ pascalize .Name }}API) *Server { 145 s := new(Server) 146 {{ if .UsePFlags }} 147 s.EnabledListeners = enabledListeners 148 s.CleanupTimeout = cleanupTimeout 149 s.GracefulTimeout = gracefulTimeout 150 s.MaxHeaderSize = maxHeaderSize 151 s.SocketPath = socketPath 152 s.Host = stringEnvOverride(host, "", "HOST") 153 s.Port = intEnvOverride(port, 0, "PORT") 154 s.ListenLimit = listenLimit 155 s.KeepAlive = keepAlive 156 s.ReadTimeout = readTimeout 157 s.WriteTimeout = writeTimeout 158 s.TLSHost = stringEnvOverride(tlsHost, s.Host, "TLS_HOST", "HOST") 159 s.TLSPort = intEnvOverride(tlsPort, 0, "TLS_PORT") 160 s.TLSCertificate = stringEnvOverride(tlsCertificate, "", "TLS_CERTIFICATE") 161 s.TLSCertificateKey = stringEnvOverride(tlsCertificateKey, "", "TLS_PRIVATE_KEY") 162 s.TLSCACertificate = stringEnvOverride(tlsCACertificate, "", "TLS_CA_CERTIFICATE") 163 s.TLSListenLimit = tlsListenLimit 164 s.TLSKeepAlive = tlsKeepAlive 165 s.TLSReadTimeout = tlsReadTimeout 166 s.TLSWriteTimeout = tlsWriteTimeout 167 {{- if .ExcludeSpec }} 168 s.Spec = specFile 169 {{- end }} 170 {{- end }} 171 s.shutdown = make(chan struct{}) 172 s.api = api 173 s.interrupt = make(chan os.Signal, 1) 174 return s 175 } 176 177 // ConfigureAPI configures the API and handlers. 178 func (s *Server) ConfigureAPI() { 179 if s.api != nil { 180 s.handler = configureAPI(s.api) 181 } 182 } 183 184 // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse 185 func (s *Server) ConfigureFlags() { 186 if s.api != nil { 187 configureFlags(s.api) 188 } 189 } 190 191 // Server for the {{ humanize .Name }} API 192 type Server struct { 193 EnabledListeners []string{{ if .UseGoStructFlags }} `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`{{ end }} 194 CleanupTimeout time.Duration{{ if .UseGoStructFlags }} `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`{{ end }} 195 GracefulTimeout time.Duration{{ if .UseGoStructFlags }} `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`{{ end }} 196 MaxHeaderSize flagext.ByteSize{{ if .UseGoStructFlags }} `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`{{ end }} 197 198 SocketPath {{ if .UsePFlags }}string{{ else }}flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/{{ dasherize .Name }}.sock"`{{ end }} 199 domainSocketL net.Listener 200 201 Host string{{ if .UseGoStructFlags }} `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`{{ end }} 202 Port int{{ if .UseGoStructFlags }} `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`{{ end }} 203 ListenLimit int{{ if .UseGoStructFlags }} `long:"listen-limit" description:"limit the number of outstanding requests"`{{ end }} 204 KeepAlive time.Duration{{ if .UseGoStructFlags }} `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`{{ end }} 205 ReadTimeout time.Duration{{ if .UseGoStructFlags }} `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`{{ end }} 206 WriteTimeout time.Duration{{ if .UseGoStructFlags }} `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`{{ end }} 207 httpServerL net.Listener 208 209 TLSHost string{{ if .UseGoStructFlags }} `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`{{ end }} 210 TLSPort int{{ if .UseGoStructFlags }} `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`{{ end }} 211 TLSCertificate {{ if .UsePFlags }}string{{ else }}flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`{{ end }} 212 TLSCertificateKey {{ if .UsePFlags }}string{{ else }}flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"`{{ end }} 213 TLSCACertificate {{ if .UsePFlags }}string{{ else }}flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`{{ end }} 214 TLSListenLimit int{{ if .UseGoStructFlags }} `long:"tls-listen-limit" description:"limit the number of outstanding requests"`{{ end }} 215 TLSKeepAlive time.Duration{{ if .UseGoStructFlags }} `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`{{ end }} 216 TLSReadTimeout time.Duration{{ if .UseGoStructFlags }} `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`{{ end }} 217 TLSWriteTimeout time.Duration{{ if .UseGoStructFlags }} `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`{{ end }} 218 httpsServerL net.Listener 219 220 {{ if .ExcludeSpec }}Spec {{ if .UsePFlags }}string{{ else }}flags.Filename `long:"spec" description:"the swagger specification to serve"`{{ end }}{{ end }} 221 api *{{ .Package }}.{{ pascalize .Name }}API 222 handler http.Handler 223 hasListeners bool 224 shutdown chan struct{} 225 shuttingDown int32 226 interrupted bool 227 interrupt chan os.Signal 228 } 229 230 // Logf logs message either via defined user logger or via system one if no user logger is defined. 231 func (s *Server) Logf(f string, args ...interface{}) { 232 if s.api != nil && s.api.Logger != nil { 233 s.api.Logger(f, args...) 234 } else { 235 log.Printf(f, args...) 236 } 237 } 238 239 // Fatalf logs message either via defined user logger or via system one if no user logger is defined. 240 // Exits with non-zero status after printing 241 func (s *Server) Fatalf(f string, args ...interface{}) { 242 if s.api != nil && s.api.Logger != nil { 243 s.api.Logger(f, args...) 244 os.Exit(1) 245 } else { 246 log.Fatalf(f, args...) 247 } 248 } 249 250 // SetAPI configures the server with the specified API. Needs to be called before Serve 251 func (s *Server) SetAPI(api *{{ .Package }}.{{ pascalize .Name }}API) { 252 if api == nil { 253 s.api = nil 254 s.handler = nil 255 return 256 } 257 258 s.api = api 259 s.api.Logger = log.Printf 260 s.handler = configureAPI(api) 261 } 262 263 func (s *Server) hasScheme(scheme string) bool { 264 schemes := s.EnabledListeners 265 if len(schemes) == 0 { 266 schemes = defaultSchemes 267 } 268 269 for _, v := range schemes { 270 if v == scheme { 271 return true 272 } 273 } 274 return false 275 } 276 277 // Serve the api 278 func (s *Server) Serve() (err error) { 279 if !s.hasListeners { 280 if err = s.Listen(); err != nil { 281 return err 282 } 283 } 284 285 // set default handler, if none is set 286 if s.handler == nil { 287 if s.api == nil { 288 return errors.New("can't create the default handler, as no api is set") 289 } 290 291 s.SetHandler(s.api.Serve(nil)) 292 } 293 294 wg := new(sync.WaitGroup) 295 once := new(sync.Once) 296 signalNotify(s.interrupt) 297 go handleInterrupt(once, s) 298 299 servers := []*http.Server{} 300 wg.Add(1) 301 go s.handleShutdown(wg, &servers) 302 303 if s.hasScheme(schemeUnix) { 304 domainSocket := new(http.Server) 305 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) 306 domainSocket.Handler = s.handler 307 if int64(s.CleanupTimeout) > 0 { 308 domainSocket.IdleTimeout = s.CleanupTimeout 309 } 310 311 configureServer(domainSocket, "unix", string(s.SocketPath)) 312 313 if os.Getuid() == 0 { 314 err := api.SetDefaultPermissions(string(s.SocketPath)) 315 if err != nil { 316 return err 317 } 318 } 319 servers = append(servers, domainSocket) 320 wg.Add(1) 321 s.Logf("Serving {{ humanize .Name }} at unix://%s", s.SocketPath) 322 go func(l net.Listener){ 323 defer wg.Done() 324 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 325 s.Fatalf("%v", err) 326 } 327 s.Logf("Stopped serving {{ humanize .Name }} at unix://%s", s.SocketPath) 328 }(s.domainSocketL) 329 } 330 331 if s.hasScheme(schemeHTTP) { 332 httpServer := new(http.Server) 333 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) 334 httpServer.ReadTimeout = s.ReadTimeout 335 httpServer.WriteTimeout = s.WriteTimeout 336 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 337 if s.ListenLimit > 0 { 338 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 339 } 340 341 if int64(s.CleanupTimeout) > 0 { 342 httpServer.IdleTimeout = s.CleanupTimeout 343 } 344 345 httpServer.Handler = s.handler 346 347 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 348 349 servers = append(servers, httpServer) 350 wg.Add(1) 351 s.Logf("Serving {{ humanize .Name }} at http://%s", s.httpServerL.Addr()) 352 go func(l net.Listener) { 353 defer wg.Done() 354 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 355 s.Fatalf("%v", err) 356 } 357 s.Logf("Stopped serving {{ humanize .Name }} at http://%s", l.Addr()) 358 }(s.httpServerL) 359 } 360 361 if s.hasScheme(schemeHTTPS) { 362 httpsServer := new(http.Server) 363 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) 364 httpsServer.ReadTimeout = s.TLSReadTimeout 365 httpsServer.WriteTimeout = s.TLSWriteTimeout 366 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 367 if s.TLSListenLimit > 0 { 368 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 369 } 370 if int64(s.CleanupTimeout) > 0 { 371 httpsServer.IdleTimeout = s.CleanupTimeout 372 } 373 httpsServer.Handler = s.handler 374 375 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 376 httpsServer.TLSConfig = &tls.Config{ 377 // Causes servers to use Go's default ciphersuite preferences, 378 // which are tuned to avoid attacks. Does nothing on clients. 379 PreferServerCipherSuites: true, 380 // Only use curves which have assembly implementations 381 // https://github.com/golang/go/tree/master/src/crypto/elliptic 382 CurvePreferences: []tls.CurveID{tls.CurveP256}, 383 {{- if .UseModernMode }} 384 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 385 NextProtos: []string{"http/1.1", "h2"}, 386 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 387 MinVersion: tls.VersionTLS12, 388 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 389 CipherSuites: []uint16{ 390 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 391 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 392 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 393 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 394 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 395 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 396 }, 397 {{- end }} 398 } 399 400 // build standard config from server options 401 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 402 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 403 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair({{ if .UseGoStructFlags }}string({{ end }}s.TLSCertificate{{ if .UseGoStructFlags }}){{ end }}, {{ if .UseGoStructFlags }}string({{ end }}s.TLSCertificateKey{{ if .UseGoStructFlags }}){{ end }}) 404 if err != nil { 405 return err 406 } 407 } 408 409 if s.TLSCACertificate != "" { 410 // include specified CA certificate 411 caCert, caCertErr := ioutil.ReadFile({{ if .UseGoStructFlags }}string({{ end }}s.TLSCACertificate{{ if .UseGoStructFlags }}){{ end }}) 412 if caCertErr != nil { 413 return caCertErr 414 } 415 caCertPool := x509.NewCertPool() 416 ok := caCertPool.AppendCertsFromPEM(caCert) 417 if !ok { 418 return fmt.Errorf("cannot parse CA certificate") 419 } 420 httpsServer.TLSConfig.ClientCAs = caCertPool 421 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 422 } 423 424 // call custom TLS configurator 425 configureTLS(httpsServer.TLSConfig) 426 427 if len(httpsServer.TLSConfig.Certificates) == 0 { 428 // after standard and custom config are passed, this ends up with no certificate 429 if s.TLSCertificate == "" { 430 if s.TLSCertificateKey == "" { 431 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 432 } 433 s.Fatalf("the required flag `--tls-certificate` was not specified") 434 } 435 if s.TLSCertificateKey == "" { 436 s.Fatalf("the required flag `--tls-key` was not specified") 437 } 438 // this happens with a wrong custom TLS configurator 439 s.Fatalf("no certificate was configured for TLS") 440 } 441 442 // must have at least one certificate or panics 443 httpsServer.TLSConfig.BuildNameToCertificate() 444 445 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 446 447 servers = append(servers, httpsServer) 448 wg.Add(1) 449 s.Logf("Serving {{ humanize .Name }} at https://%s", s.httpsServerL.Addr()) 450 go func(l net.Listener) { 451 defer wg.Done() 452 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 453 s.Fatalf("%v", err) 454 } 455 s.Logf("Stopped serving {{ humanize .Name }} at https://%s", l.Addr()) 456 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 457 } 458 459 wg.Wait() 460 return nil 461 } 462 463 // Listen creates the listeners for the server 464 func (s *Server) Listen() error { 465 if s.hasListeners { // already done this 466 return nil 467 } 468 469 if s.hasScheme(schemeHTTPS) { 470 // Use http host if https host wasn't defined 471 if s.TLSHost == "" { 472 s.TLSHost = s.Host 473 } 474 // Use http listen limit if https listen limit wasn't defined 475 if s.TLSListenLimit == 0 { 476 s.TLSListenLimit = s.ListenLimit 477 } 478 // Use http tcp keep alive if https tcp keep alive wasn't defined 479 if int64(s.TLSKeepAlive) == 0 { 480 s.TLSKeepAlive = s.KeepAlive 481 } 482 // Use http read timeout if https read timeout wasn't defined 483 if int64(s.TLSReadTimeout) == 0 { 484 s.TLSReadTimeout = s.ReadTimeout 485 } 486 // Use http write timeout if https write timeout wasn't defined 487 if int64(s.TLSWriteTimeout) == 0 { 488 s.TLSWriteTimeout = s.WriteTimeout 489 } 490 } 491 492 if s.hasScheme(schemeUnix) { 493 domSockListener, err := net.Listen("unix", string(s.SocketPath)) 494 if err != nil { 495 return err 496 } 497 s.domainSocketL = domSockListener 498 } 499 500 if s.hasScheme(schemeHTTP) { 501 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 502 if err != nil { 503 return err 504 } 505 506 h, p, err := swag.SplitHostPort(listener.Addr().String()) 507 if err != nil { 508 return err 509 } 510 s.Host = h 511 s.Port = p 512 s.httpServerL = listener 513 } 514 515 if s.hasScheme(schemeHTTPS) { 516 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 517 if err != nil { 518 return err 519 } 520 521 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 522 if err != nil { 523 return err 524 } 525 s.TLSHost = sh 526 s.TLSPort = sp 527 s.httpsServerL = tlsListener 528 } 529 530 s.hasListeners = true 531 return nil 532 } 533 534 // Shutdown server and clean up resources 535 func (s *Server) Shutdown() error { 536 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { 537 close(s.shutdown) 538 } 539 return nil 540 } 541 542 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { 543 // wg.Done must occur last, after s.api.ServerShutdown() 544 // (to preserve old behaviour) 545 defer wg.Done() 546 547 <-s.shutdown 548 549 servers := *serversPtr 550 551 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 552 defer cancel() 553 554 shutdownChan := make(chan bool) 555 for i := range servers { 556 server := servers[i] 557 go func() { 558 var success bool 559 defer func() { 560 shutdownChan <- success 561 }() 562 if err := server.Shutdown(ctx); err != nil { 563 // Error from closing listeners, or context timeout: 564 s.Logf("HTTP server Shutdown: %v", err) 565 } else { 566 success = true 567 } 568 }() 569 } 570 571 // Wait until all listeners have successfully shut down before calling ServerShutdown 572 success := true 573 for range servers { 574 success = success && <-shutdownChan 575 } 576 if success { 577 s.api.ServerShutdown() 578 } 579 } 580 581 // GetHandler returns a handler useful for testing 582 func (s *Server) GetHandler() http.Handler { 583 return s.handler 584 } 585 586 // SetHandler allows for setting a http handler on this server 587 func (s *Server) SetHandler(handler http.Handler) { 588 s.handler = handler 589 } 590 591 // UnixListener returns the domain socket listener 592 func (s *Server) UnixListener() (net.Listener, error) { 593 if !s.hasListeners { 594 if err := s.Listen(); err != nil { 595 return nil, err 596 } 597 } 598 return s.domainSocketL, nil 599 } 600 601 // HTTPListener returns the http listener 602 func (s *Server) HTTPListener() (net.Listener, error) { 603 if !s.hasListeners { 604 if err := s.Listen(); err != nil { 605 return nil, err 606 } 607 } 608 return s.httpServerL, nil 609 } 610 611 // TLSListener returns the https listener 612 func (s *Server) TLSListener() (net.Listener, error) { 613 if !s.hasListeners { 614 if err := s.Listen(); err != nil { 615 return nil, err 616 } 617 } 618 return s.httpsServerL, nil 619 } 620 621 func handleInterrupt(once *sync.Once, s *Server) { 622 once.Do(func(){ 623 for range s.interrupt { 624 if s.interrupted { 625 s.Logf("Server already shutting down") 626 continue 627 } 628 s.interrupted = true 629 s.Logf("Shutting down... ") 630 if err := s.Shutdown(); err != nil { 631 s.Logf("HTTP server Shutdown: %v", err) 632 } 633 } 634 }) 635 } 636 637 func signalNotify(interrupt chan<- os.Signal) { 638 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 639 }