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