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