github.com/kaisawind/go-swagger@v0.19.0/generator/templates/server/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 connections" 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 servers = append(servers, domainSocket) 314 wg.Add(1) 315 s.Logf("Serving {{ humanize .Name }} at unix://%s", s.SocketPath) 316 go func(l net.Listener){ 317 defer wg.Done() 318 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 319 s.Fatalf("%v", err) 320 } 321 s.Logf("Stopped serving {{ humanize .Name }} at unix://%s", s.SocketPath) 322 }(s.domainSocketL) 323 } 324 325 if s.hasScheme(schemeHTTP) { 326 httpServer := new(http.Server) 327 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) 328 httpServer.ReadTimeout = s.ReadTimeout 329 httpServer.WriteTimeout = s.WriteTimeout 330 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 331 if s.ListenLimit > 0 { 332 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 333 } 334 335 if int64(s.CleanupTimeout) > 0 { 336 httpServer.IdleTimeout = s.CleanupTimeout 337 } 338 339 httpServer.Handler = s.handler 340 341 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 342 343 servers = append(servers, httpServer) 344 wg.Add(1) 345 s.Logf("Serving {{ humanize .Name }} at http://%s", s.httpServerL.Addr()) 346 go func(l net.Listener) { 347 defer wg.Done() 348 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 349 s.Fatalf("%v", err) 350 } 351 s.Logf("Stopped serving {{ humanize .Name }} at http://%s", l.Addr()) 352 }(s.httpServerL) 353 } 354 355 if s.hasScheme(schemeHTTPS) { 356 httpsServer := new(http.Server) 357 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) 358 httpsServer.ReadTimeout = s.TLSReadTimeout 359 httpsServer.WriteTimeout = s.TLSWriteTimeout 360 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 361 if s.TLSListenLimit > 0 { 362 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 363 } 364 if int64(s.CleanupTimeout) > 0 { 365 httpsServer.IdleTimeout = s.CleanupTimeout 366 } 367 httpsServer.Handler = s.handler 368 369 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 370 httpsServer.TLSConfig = &tls.Config{ 371 // Causes servers to use Go's default ciphersuite preferences, 372 // which are tuned to avoid attacks. Does nothing on clients. 373 PreferServerCipherSuites: true, 374 // Only use curves which have assembly implementations 375 // https://github.com/golang/go/tree/master/src/crypto/elliptic 376 CurvePreferences: []tls.CurveID{tls.CurveP256}, 377 {{- if .UseModernMode }} 378 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 379 NextProtos: []string{"http/1.1", "h2"}, 380 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 381 MinVersion: tls.VersionTLS12, 382 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 383 CipherSuites: []uint16{ 384 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 385 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 386 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 387 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 388 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 389 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 390 }, 391 {{- end }} 392 } 393 394 // build standard config from server options 395 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 396 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 397 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 }}) 398 if err != nil { 399 return err 400 } 401 } 402 403 if s.TLSCACertificate != "" { 404 // include specified CA certificate 405 caCert, caCertErr := ioutil.ReadFile({{ if .UseGoStructFlags }}string({{ end }}s.TLSCACertificate{{ if .UseGoStructFlags }}){{ end }}) 406 if caCertErr != nil { 407 return caCertErr 408 } 409 caCertPool := x509.NewCertPool() 410 ok := caCertPool.AppendCertsFromPEM(caCert) 411 if !ok { 412 return fmt.Errorf("cannot parse CA certificate") 413 } 414 httpsServer.TLSConfig.ClientCAs = caCertPool 415 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 416 } 417 418 // call custom TLS configurator 419 configureTLS(httpsServer.TLSConfig) 420 421 if len(httpsServer.TLSConfig.Certificates) == 0 { 422 // after standard and custom config are passed, this ends up with no certificate 423 if s.TLSCertificate == "" { 424 if s.TLSCertificateKey == "" { 425 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 426 } 427 s.Fatalf("the required flag `--tls-certificate` was not specified") 428 } 429 if s.TLSCertificateKey == "" { 430 s.Fatalf("the required flag `--tls-key` was not specified") 431 } 432 // this happens with a wrong custom TLS configurator 433 s.Fatalf("no certificate was configured for TLS") 434 } 435 436 // must have at least one certificate or panics 437 httpsServer.TLSConfig.BuildNameToCertificate() 438 439 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 440 441 servers = append(servers, httpsServer) 442 wg.Add(1) 443 s.Logf("Serving {{ humanize .Name }} at https://%s", s.httpsServerL.Addr()) 444 go func(l net.Listener) { 445 defer wg.Done() 446 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 447 s.Fatalf("%v", err) 448 } 449 s.Logf("Stopped serving {{ humanize .Name }} at https://%s", l.Addr()) 450 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 451 } 452 453 wg.Wait() 454 return nil 455 } 456 457 // Listen creates the listeners for the server 458 func (s *Server) Listen() error { 459 if s.hasListeners { // already done this 460 return nil 461 } 462 463 if s.hasScheme(schemeHTTPS) { 464 // Use http host if https host wasn't defined 465 if s.TLSHost == "" { 466 s.TLSHost = s.Host 467 } 468 // Use http listen limit if https listen limit wasn't defined 469 if s.TLSListenLimit == 0 { 470 s.TLSListenLimit = s.ListenLimit 471 } 472 // Use http tcp keep alive if https tcp keep alive wasn't defined 473 if int64(s.TLSKeepAlive) == 0 { 474 s.TLSKeepAlive = s.KeepAlive 475 } 476 // Use http read timeout if https read timeout wasn't defined 477 if int64(s.TLSReadTimeout) == 0 { 478 s.TLSReadTimeout = s.ReadTimeout 479 } 480 // Use http write timeout if https write timeout wasn't defined 481 if int64(s.TLSWriteTimeout) == 0 { 482 s.TLSWriteTimeout = s.WriteTimeout 483 } 484 } 485 486 if s.hasScheme(schemeUnix) { 487 domSockListener, err := net.Listen("unix", string(s.SocketPath)) 488 if err != nil { 489 return err 490 } 491 s.domainSocketL = domSockListener 492 } 493 494 if s.hasScheme(schemeHTTP) { 495 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 496 if err != nil { 497 return err 498 } 499 500 h, p, err := swag.SplitHostPort(listener.Addr().String()) 501 if err != nil { 502 return err 503 } 504 s.Host = h 505 s.Port = p 506 s.httpServerL = listener 507 } 508 509 if s.hasScheme(schemeHTTPS) { 510 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 511 if err != nil { 512 return err 513 } 514 515 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 516 if err != nil { 517 return err 518 } 519 s.TLSHost = sh 520 s.TLSPort = sp 521 s.httpsServerL = tlsListener 522 } 523 524 s.hasListeners = true 525 return nil 526 } 527 528 // Shutdown server and clean up resources 529 func (s *Server) Shutdown() error { 530 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { 531 close(s.shutdown) 532 } 533 return nil 534 } 535 536 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { 537 // wg.Done must occur last, after s.api.ServerShutdown() 538 // (to preserve old behaviour) 539 defer wg.Done() 540 541 <-s.shutdown 542 543 servers := *serversPtr 544 545 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 546 defer cancel() 547 548 shutdownChan := make(chan bool) 549 for i := range servers { 550 server := servers[i] 551 go func() { 552 var success bool 553 defer func() { 554 shutdownChan <- success 555 }() 556 if err := server.Shutdown(ctx); err != nil { 557 // Error from closing listeners, or context timeout: 558 s.Logf("HTTP server Shutdown: %v", err) 559 } else { 560 success = true 561 } 562 }() 563 } 564 565 // Wait until all listeners have successfully shut down before calling ServerShutdown 566 success := true 567 for range servers { 568 success = success && <-shutdownChan 569 } 570 if success { 571 s.api.ServerShutdown() 572 } 573 } 574 575 // GetHandler returns a handler useful for testing 576 func (s *Server) GetHandler() http.Handler { 577 return s.handler 578 } 579 580 // SetHandler allows for setting a http handler on this server 581 func (s *Server) SetHandler(handler http.Handler) { 582 s.handler = handler 583 } 584 585 // UnixListener returns the domain socket listener 586 func (s *Server) UnixListener() (net.Listener, error) { 587 if !s.hasListeners { 588 if err := s.Listen(); err != nil { 589 return nil, err 590 } 591 } 592 return s.domainSocketL, nil 593 } 594 595 // HTTPListener returns the http listener 596 func (s *Server) HTTPListener() (net.Listener, error) { 597 if !s.hasListeners { 598 if err := s.Listen(); err != nil { 599 return nil, err 600 } 601 } 602 return s.httpServerL, nil 603 } 604 605 // TLSListener returns the https listener 606 func (s *Server) TLSListener() (net.Listener, error) { 607 if !s.hasListeners { 608 if err := s.Listen(); err != nil { 609 return nil, err 610 } 611 } 612 return s.httpsServerL, nil 613 } 614 615 func handleInterrupt(once *sync.Once, s *Server) { 616 once.Do(func(){ 617 for _ = range s.interrupt { 618 if s.interrupted { 619 s.Logf("Server already shutting down") 620 continue 621 } 622 s.interrupted = true 623 s.Logf("Shutting down... ") 624 if err := s.Shutdown(); err != nil { 625 s.Logf("HTTP server Shutdown: %v", err) 626 } 627 } 628 }) 629 } 630 631 func signalNotify(interrupt chan<- os.Signal) { 632 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 633 }