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