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