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  }