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