github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/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 "log" 12 "net" 13 "net/http" 14 "os" 15 "os/signal" 16 "strconv" 17 "sync" 18 "sync/atomic" 19 "syscall" 20 "time" 21 22 "github.com/go-openapi/runtime/flagext" 23 "github.com/go-openapi/swag" 24 flag "github.com/spf13/pflag" 25 "golang.org/x/net/netutil" 26 27 "github.com/thetreep/go-swagger/examples/todo-list/restapi/operations" 28 ) 29 30 const ( 31 schemeHTTP = "http" 32 schemeHTTPS = "https" 33 schemeUnix = "unix" 34 ) 35 36 var defaultSchemes []string 37 38 func init() { 39 defaultSchemes = []string{ 40 schemeHTTP, 41 schemeHTTPS, 42 schemeUnix, 43 } 44 } 45 46 var ( 47 enabledListeners []string 48 cleanupTimeout time.Duration 49 gracefulTimeout time.Duration 50 maxHeaderSize flagext.ByteSize 51 52 socketPath string 53 54 host string 55 port int 56 listenLimit int 57 keepAlive time.Duration 58 readTimeout time.Duration 59 writeTimeout time.Duration 60 61 tlsHost string 62 tlsPort int 63 tlsListenLimit int 64 tlsKeepAlive time.Duration 65 tlsReadTimeout time.Duration 66 tlsWriteTimeout time.Duration 67 tlsCertificate string 68 tlsCertificateKey string 69 tlsCACertificate string 70 ) 71 72 func init() { 73 maxHeaderSize = flagext.ByteSize(1000000) 74 75 flag.StringSliceVar(&enabledListeners, "scheme", defaultSchemes, "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec") 76 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.handler = configureAPI(api) 242 } 243 244 func (s *Server) hasScheme(scheme string) bool { 245 schemes := s.EnabledListeners 246 if len(schemes) == 0 { 247 schemes = defaultSchemes 248 } 249 250 for _, v := range schemes { 251 if v == scheme { 252 return true 253 } 254 } 255 return false 256 } 257 258 // Serve the api 259 func (s *Server) Serve() (err error) { 260 if !s.hasListeners { 261 if err = s.Listen(); err != nil { 262 return err 263 } 264 } 265 266 // set default handler, if none is set 267 if s.handler == nil { 268 if s.api == nil { 269 return errors.New("can't create the default handler, as no api is set") 270 } 271 272 s.SetHandler(s.api.Serve(nil)) 273 } 274 275 wg := new(sync.WaitGroup) 276 once := new(sync.Once) 277 signalNotify(s.interrupt) 278 go handleInterrupt(once, s) 279 280 servers := []*http.Server{} 281 282 if s.hasScheme(schemeUnix) { 283 domainSocket := new(http.Server) 284 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) 285 domainSocket.Handler = s.handler 286 if int64(s.CleanupTimeout) > 0 { 287 domainSocket.IdleTimeout = s.CleanupTimeout 288 } 289 290 configureServer(domainSocket, "unix", string(s.SocketPath)) 291 292 servers = append(servers, domainSocket) 293 wg.Add(1) 294 s.Logf("Serving todo list at unix://%s", s.SocketPath) 295 go func(l net.Listener) { 296 defer wg.Done() 297 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 298 s.Fatalf("%v", err) 299 } 300 s.Logf("Stopped serving todo list at unix://%s", s.SocketPath) 301 }(s.domainSocketL) 302 } 303 304 if s.hasScheme(schemeHTTP) { 305 httpServer := new(http.Server) 306 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) 307 httpServer.ReadTimeout = s.ReadTimeout 308 httpServer.WriteTimeout = s.WriteTimeout 309 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 310 if s.ListenLimit > 0 { 311 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 312 } 313 314 if int64(s.CleanupTimeout) > 0 { 315 httpServer.IdleTimeout = s.CleanupTimeout 316 } 317 318 httpServer.Handler = s.handler 319 320 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 321 322 servers = append(servers, httpServer) 323 wg.Add(1) 324 s.Logf("Serving todo list at http://%s", s.httpServerL.Addr()) 325 go func(l net.Listener) { 326 defer wg.Done() 327 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 328 s.Fatalf("%v", err) 329 } 330 s.Logf("Stopped serving todo list at http://%s", l.Addr()) 331 }(s.httpServerL) 332 } 333 334 if s.hasScheme(schemeHTTPS) { 335 httpsServer := new(http.Server) 336 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) 337 httpsServer.ReadTimeout = s.TLSReadTimeout 338 httpsServer.WriteTimeout = s.TLSWriteTimeout 339 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 340 if s.TLSListenLimit > 0 { 341 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 342 } 343 if int64(s.CleanupTimeout) > 0 { 344 httpsServer.IdleTimeout = s.CleanupTimeout 345 } 346 httpsServer.Handler = s.handler 347 348 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 349 httpsServer.TLSConfig = &tls.Config{ 350 // Causes servers to use Go's default ciphersuite preferences, 351 // which are tuned to avoid attacks. Does nothing on clients. 352 PreferServerCipherSuites: true, 353 // Only use curves which have assembly implementations 354 // https://github.com/golang/go/tree/master/src/crypto/elliptic 355 CurvePreferences: []tls.CurveID{tls.CurveP256}, 356 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 357 NextProtos: []string{"h2", "http/1.1"}, 358 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 359 MinVersion: tls.VersionTLS12, 360 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 361 CipherSuites: []uint16{ 362 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 363 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 364 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 365 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 366 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 367 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 368 }, 369 } 370 371 // build standard config from server options 372 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 373 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 374 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey) 375 if err != nil { 376 return err 377 } 378 } 379 380 if s.TLSCACertificate != "" { 381 // include specified CA certificate 382 caCert, caCertErr := os.ReadFile(s.TLSCACertificate) 383 if caCertErr != nil { 384 return caCertErr 385 } 386 caCertPool := x509.NewCertPool() 387 ok := caCertPool.AppendCertsFromPEM(caCert) 388 if !ok { 389 return fmt.Errorf("cannot parse CA certificate") 390 } 391 httpsServer.TLSConfig.ClientCAs = caCertPool 392 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 393 } 394 395 // call custom TLS configurator 396 configureTLS(httpsServer.TLSConfig) 397 398 if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil { 399 // after standard and custom config are passed, this ends up with no certificate 400 if s.TLSCertificate == "" { 401 if s.TLSCertificateKey == "" { 402 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 403 } 404 s.Fatalf("the required flag `--tls-certificate` was not specified") 405 } 406 if s.TLSCertificateKey == "" { 407 s.Fatalf("the required flag `--tls-key` was not specified") 408 } 409 // this happens with a wrong custom TLS configurator 410 s.Fatalf("no certificate was configured for TLS") 411 } 412 413 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 414 415 servers = append(servers, httpsServer) 416 wg.Add(1) 417 s.Logf("Serving todo list at https://%s", s.httpsServerL.Addr()) 418 go func(l net.Listener) { 419 defer wg.Done() 420 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 421 s.Fatalf("%v", err) 422 } 423 s.Logf("Stopped serving todo list at https://%s", l.Addr()) 424 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 425 } 426 427 wg.Add(1) 428 go s.handleShutdown(wg, &servers) 429 430 wg.Wait() 431 return nil 432 } 433 434 // Listen creates the listeners for the server 435 func (s *Server) Listen() error { 436 if s.hasListeners { // already done this 437 return nil 438 } 439 440 if s.hasScheme(schemeHTTPS) { 441 // Use http host if https host wasn't defined 442 if s.TLSHost == "" { 443 s.TLSHost = s.Host 444 } 445 // Use http listen limit if https listen limit wasn't defined 446 if s.TLSListenLimit == 0 { 447 s.TLSListenLimit = s.ListenLimit 448 } 449 // Use http tcp keep alive if https tcp keep alive wasn't defined 450 if int64(s.TLSKeepAlive) == 0 { 451 s.TLSKeepAlive = s.KeepAlive 452 } 453 // Use http read timeout if https read timeout wasn't defined 454 if int64(s.TLSReadTimeout) == 0 { 455 s.TLSReadTimeout = s.ReadTimeout 456 } 457 // Use http write timeout if https write timeout wasn't defined 458 if int64(s.TLSWriteTimeout) == 0 { 459 s.TLSWriteTimeout = s.WriteTimeout 460 } 461 } 462 463 if s.hasScheme(schemeUnix) { 464 domSockListener, err := net.Listen("unix", string(s.SocketPath)) 465 if err != nil { 466 return err 467 } 468 s.domainSocketL = domSockListener 469 } 470 471 if s.hasScheme(schemeHTTP) { 472 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 473 if err != nil { 474 return err 475 } 476 477 h, p, err := swag.SplitHostPort(listener.Addr().String()) 478 if err != nil { 479 return err 480 } 481 s.Host = h 482 s.Port = p 483 s.httpServerL = listener 484 } 485 486 if s.hasScheme(schemeHTTPS) { 487 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 488 if err != nil { 489 return err 490 } 491 492 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 493 if err != nil { 494 return err 495 } 496 s.TLSHost = sh 497 s.TLSPort = sp 498 s.httpsServerL = tlsListener 499 } 500 501 s.hasListeners = true 502 return nil 503 } 504 505 // Shutdown server and clean up resources 506 func (s *Server) Shutdown() error { 507 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { 508 close(s.shutdown) 509 } 510 return nil 511 } 512 513 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { 514 // wg.Done must occur last, after s.api.ServerShutdown() 515 // (to preserve old behaviour) 516 defer wg.Done() 517 518 <-s.shutdown 519 520 servers := *serversPtr 521 522 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 523 defer cancel() 524 525 // first execute the pre-shutdown hook 526 s.api.PreServerShutdown() 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 }