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  }