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