// Code generated by go-swagger; DO NOT EDIT.


{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}


package {{ .APIPackage }}

import (
	"context"
	"crypto/tls"
	"errors"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

    "github.com/go-openapi/runtime/flagext"
    "github.com/go-openapi/swag"
    "golang.org/x/net/netutil"

  {{ imports .DefaultImports }}
  {{ imports .Imports }}
)

const (
	schemeHTTP  = "http"
	schemeHTTPS = "https"
)

var defaultSchemes []string

func init() {
	defaultSchemes = []string{ {{ if (hasInsecure .Schemes) }}
		schemeHTTP,{{ end}}{{ if (hasSecure .Schemes) }}
		schemeHTTPS,{{ end }}
	}
}

type ServerConfig struct {
  EnabledListeners []string
  CleanupTimeout   time.Duration
  GracefulTimeout  time.Duration
  MaxHeaderSize    int

  ListenAddress string
  ListenLimit   int
  KeepAlive     time.Duration
  ReadTimeout   time.Duration
  WriteTimeout  time.Duration

  TLSListenAddress  string
  TLSListenLimit    int
  TLSKeepAlive      time.Duration
  TLSReadTimeout    time.Duration
  TLSWriteTimeout   time.Duration
  TLSCertificate    string
  TLSCertificateKey string
  TLSCACertificate  string

  SuccessfulStartCallback func()
}

// NewServer creates a new api {{ humanize .Name }} server but does not configure it
func NewServer(api *{{ .APIPackageAlias }}.{{ pascalize .Name }}API, cfg *ServerConfig) *Server {
	s := new(Server)
	s.EnabledListeners = cfg.EnabledListeners
	s.CleanupTimeout = cfg.CleanupTimeout
	s.GracefulTimeout = cfg.GracefulTimeout
	s.MaxHeaderSize = cfg.MaxHeaderSize
	s.ListenAddress = cfg.ListenAddress
	s.ListenLimit = cfg.ListenLimit
	s.KeepAlive = cfg.KeepAlive
	s.ReadTimeout = cfg.ReadTimeout
	s.WriteTimeout = cfg.WriteTimeout
	s.TLSListenAddress = cfg.TLSListenAddress
	if len(s.TLSListenAddress) == 0 {
        s.TLSListenAddress = s.ListenAddress
    }
	s.TLSCertificate = cfg.TLSCertificate
	s.TLSCertificateKey = cfg.TLSCertificateKey
	s.TLSCACertificate = cfg.TLSCACertificate
	s.TLSListenLimit = cfg.TLSListenLimit
	s.TLSKeepAlive = cfg.TLSKeepAlive
	s.TLSReadTimeout = cfg.TLSReadTimeout
	s.TLSWriteTimeout = cfg.TLSWriteTimeout
	s.shutdown = make(chan struct{})
	s.api = api
	s.startCallback = cfg.SuccessfulStartCallback
	s.interrupt = make(chan os.Signal, 1)
	return s
}

// ConfigureAPI configures the API and handlers.
func (s *Server) ConfigureAPI(fn func (*{{ .APIPackageAlias }}.{{ pascalize .Name }}API) http.Handler) {
    if s.api != nil {
        s.handler = fn(s.api)
    }
}

// Server for the {{ humanize .Name }} API
type Server struct {
	EnabledListeners []string
	CleanupTimeout   time.Duration
	GracefulTimeout  time.Duration
	MaxHeaderSize    int

	ListenAddress string
	ListenLimit  int
	KeepAlive    time.Duration
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
	httpServerL   net.Listener

    TLSListenAddress  string
	TLSCertificate    string
	TLSCertificateKey string
	TLSCACertificate  string
	TLSListenLimit    int
	TLSKeepAlive      time.Duration
	TLSReadTimeout    time.Duration
	TLSWriteTimeout   time.Duration
	httpsServerL  net.Listener

    startCallback func()

    cfgTLSFn    func (tlsConfig *tls.Config)
    cfgServerFn func(s *http.Server, scheme, addr string)

	api               *{{ .APIPackageAlias }}.{{ pascalize .Name }}API
	handler           http.Handler
	hasListeners      bool
	shutdown          chan struct{}
	shuttingDown      int32
	interrupted       bool
	interrupt         chan os.Signal
}

// Logf logs message either via defined user logger or via system one if no user logger is defined.
func (s *Server) Logf(f string, args ...interface{}) {
	if s.api != nil && s.api.Logger != nil {
		s.api.Logger(f, args...)
	} else {
		log.Printf(f, args...)
	}
}

// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
// Exits with non-zero status after printing
func (s *Server) Fatalf(f string, args ...interface{}) {
	if s.api != nil && s.api.Logger != nil {
		s.api.Logger(f, args...)
		os.Exit(1)
	} else {
		log.Fatalf(f, args...)
	}
}

func (s *Server) hasScheme(scheme string) bool {
	schemes := s.EnabledListeners
	if len(schemes) == 0 {
		schemes = defaultSchemes
	}

	for _, v := range schemes {
		if v == scheme {
			return true
		}
	}
	return false
}

// Serve the api
func (s *Server) Serve() (err error) {
	if !s.hasListeners {
		if err = s.Listen(); err != nil {
		  return err
		}
	}

	// set default handler, if none is set
	if s.handler == nil {
		if s.api == nil {
			return errors.New("can't create the default handler, as no api is set")
		}

		s.SetHandler(s.api.Serve(nil))
	}

	wg := new(sync.WaitGroup)
	once := new(sync.Once)
	signalNotify(s.interrupt)
	go handleInterrupt(once, s)

	servers := []*http.Server{}

	if s.hasScheme(schemeHTTP) {
		httpServer := new(http.Server)
		httpServer.MaxHeaderBytes = s.MaxHeaderSize
		httpServer.ReadTimeout = s.ReadTimeout
		httpServer.WriteTimeout = s.WriteTimeout
		httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
		if s.ListenLimit > 0 {
			s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
		}

		if int64(s.CleanupTimeout) > 0 {
			httpServer.IdleTimeout = s.CleanupTimeout
		}

		httpServer.Handler = s.handler

        if s.cfgServerFn !=nil {
            s.cfgServerFn(httpServer, "http", s.httpServerL.Addr().String())
        }

		servers = append(servers, httpServer)
		wg.Add(1)
		s.Logf("Serving {{ humanize .Name }} at http://%s", s.httpServerL.Addr())
		go func(l net.Listener) {
			defer wg.Done()
			if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
				s.Fatalf("%v", err)
			}
			s.Logf("Stopped serving {{ humanize .Name }} at http://%s", l.Addr())
		}(s.httpServerL)
	}

	if s.hasScheme(schemeHTTPS) {
		httpsServer := new(http.Server)
		httpsServer.MaxHeaderBytes = s.MaxHeaderSize
		httpsServer.ReadTimeout = s.TLSReadTimeout
		httpsServer.WriteTimeout = s.TLSWriteTimeout
		httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
		if s.TLSListenLimit > 0 {
			s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
		}
		if int64(s.CleanupTimeout) > 0 {
			httpsServer.IdleTimeout = s.CleanupTimeout
		}
		httpsServer.Handler = s.handler

		// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
		httpsServer.TLSConfig = &tls.Config{
			// Causes servers to use Go's default ciphersuite preferences,
			// which are tuned to avoid attacks. Does nothing on clients.
			PreferServerCipherSuites: true,
			// Only use curves which have assembly implementations
			// https://github.com/golang/go/tree/master/src/crypto/elliptic
			CurvePreferences: []tls.CurveID{tls.CurveP256},
		{{- if .UseModernMode }}
			// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
			NextProtos: []string{"h2", "http/1.1"},
			// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
			MinVersion: tls.VersionTLS12,
			// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
				tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
			},
		{{- end }}
		}

		// build standard config from server options
		if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
			httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
			httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey)
			if err != nil {
				return err
			}
		}

		if s.TLSCACertificate != "" {
			// include specified CA certificate
			caCert, caCertErr := ioutil.ReadFile(s.TLSCACertificate)
			if caCertErr != nil {
				return caCertErr
			}
			caCertPool := x509.NewCertPool()
			ok := caCertPool.AppendCertsFromPEM(caCert)
			if !ok {
				return fmt.Errorf("cannot parse CA certificate")
			}
			httpsServer.TLSConfig.ClientCAs = caCertPool
			httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
		}

		// call custom TLS configurator
		if s.cfgTLSFn != nil {
		    s.cfgTLSFn(httpsServer.TLSConfig)
		}

		if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
			// after standard and custom config are passed, this ends up with no certificate
			if s.TLSCertificate == "" {
				if s.TLSCertificateKey == "" {
					s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
				}
				s.Fatalf("the required flag `--tls-certificate` was not specified")
			}
			if s.TLSCertificateKey == "" {
				s.Fatalf("the required flag `--tls-key` was not specified")
			}
			// this happens with a wrong custom TLS configurator
			s.Fatalf("no certificate was configured for TLS")
		}

        if s.cfgServerFn !=nil {
            s.cfgServerFn(httpsServer, "https", s.httpsServerL.Addr().String())
        }

		servers = append(servers, httpsServer)
		wg.Add(1)
		s.Logf("Serving {{ humanize .Name }} at https://%s", s.httpsServerL.Addr())
		go func(l net.Listener) {
			defer wg.Done()
			if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
				s.Fatalf("%v", err)
			}
			s.Logf("Stopped serving {{ humanize .Name }} at https://%s", l.Addr())
		}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
	}

	wg.Add(1)
	go s.handleShutdown(wg, &servers)

    if s.startCallback != nil {
        s.startCallback()
    }

	wg.Wait()
	return nil
}

// The TLS configuration before HTTPS server starts.
func (s *Server) ConfigureTLS(cfgTLS func (tlsConfig *tls.Config)) {
  s.cfgTLSFn = cfgTLS
}

// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix".
func (s *Server) ConfigureServer(cfgServer func (s *http.Server, scheme, addr string)) {
  s.cfgServerFn = cfgServer
}

// Listen creates the listeners for the server
func (s *Server) Listen() error {
  if s.hasListeners { // already done this
    return nil
  }

  if s.hasScheme(schemeHTTPS) {
		// Use http listen limit if https listen limit wasn't defined
		if s.TLSListenLimit == 0 {
			s.TLSListenLimit = s.ListenLimit
		}
		// Use http tcp keep alive if https tcp keep alive wasn't defined
		if int64(s.TLSKeepAlive) == 0 {
			s.TLSKeepAlive = s.KeepAlive
		}
		// Use http read timeout if https read timeout wasn't defined
		if int64(s.TLSReadTimeout) == 0 {
			s.TLSReadTimeout = s.ReadTimeout
		}
		// Use http write timeout if https write timeout wasn't defined
		if int64(s.TLSWriteTimeout) == 0 {
			s.TLSWriteTimeout = s.WriteTimeout
		}
  }

  if s.hasScheme(schemeHTTP) {
    listener, err := net.Listen("tcp", s.ListenAddress)
    if err != nil {
      return err
    }

    _, _, err = swag.SplitHostPort(listener.Addr().String())
    if err != nil {
      return err
    }
    s.httpServerL = listener
  }

  if s.hasScheme(schemeHTTPS) {
    tlsListener, err := net.Listen("tcp", s.TLSListenAddress)
    if err != nil {
      return err
    }

    _, _, err = swag.SplitHostPort(tlsListener.Addr().String())
    if err != nil {
      return err
    }
    s.httpsServerL = tlsListener
  }

  s.hasListeners = true
	return nil
}

// Shutdown server and clean up resources
func (s *Server) Shutdown() error {
	if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
		close(s.shutdown)
	}
	return nil
}

func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
	// wg.Done must occur last, after s.api.ServerShutdown()
	// (to preserve old behaviour)
	defer wg.Done()

	<-s.shutdown

	servers := *serversPtr

	ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
	defer cancel()

  // first execute the pre-shutdown hook
	s.api.PreServerShutdown()

	shutdownChan := make(chan bool)
	for i := range servers {
		server := servers[i]
		go func() {
			var success bool
			defer func() {
				shutdownChan <- success
			}()
			if err := server.Shutdown(ctx); err != nil {
				// Error from closing listeners, or context timeout:
				s.Logf("HTTP server Shutdown: %v", err)
			} else {
				success = true
			}
		}()
	}

	// Wait until all listeners have successfully shut down before calling ServerShutdown
	success := true
	for range servers {
		success = success && <-shutdownChan
	}
	if success {
		s.api.ServerShutdown()
	}
}

// GetHandler returns a handler useful for testing
func (s *Server) GetHandler() http.Handler {
	return s.handler
}

// SetHandler allows for setting a http handler on this server
func (s *Server) SetHandler(handler http.Handler) {
	s.handler = handler
}

// HTTPListener returns the http listener
func (s *Server) HTTPListener() (net.Listener, error) {
	if !s.hasListeners {
		if err := s.Listen(); err != nil {
			return nil, err
		}
	}
	return s.httpServerL, nil
}

// TLSListener returns the https listener
func (s *Server) TLSListener() (net.Listener, error) {
	if !s.hasListeners {
		if err := s.Listen(); err != nil {
			return nil, err
		}
	}
	return s.httpsServerL, nil
}

func handleInterrupt(once *sync.Once, s *Server) {
	once.Do(func(){
	for range s.interrupt {
		if s.interrupted {
			s.Logf("Server already shutting down")
			continue
		}
		s.interrupted = true
		s.Logf("Shutting down... ")
		if err := s.Shutdown(); err != nil {
			s.Logf("HTTP server Shutdown: %v", err)
		}
	}
	})
}

func signalNotify(interrupt chan<- os.Signal) {
	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
}