Skip to content

Instantly share code, notes, and snippets.

@Lajule
Last active April 12, 2023 00:25
Show Gist options
  • Select an option

  • Save Lajule/e7f98d5839056d83bb286764a307570a to your computer and use it in GitHub Desktop.

Select an option

Save Lajule/e7f98d5839056d83bb286764a307570a to your computer and use it in GitHub Desktop.

Revisions

  1. Lajule revised this gist Sep 5, 2022. 1 changed file with 0 additions and 20 deletions.
    20 changes: 0 additions & 20 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -1,20 +0,0 @@
    package main

    import (
    "net/http"
    "time"

    "github.com/gin-gonic/gin"

    "github.com/Lajule/tmp/server"
    )

    func main() {
    srv := server.NewServer(":8080", "", nil, nil)

    srv.Handler.(*gin.Engine).GET("/", func(c *gin.Context) {
    c.Status(http.StatusOK)
    })

    srv.Start("", "", 5*time.Second)
    }
  2. Lajule revised this gist Sep 5, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -10,11 +10,11 @@ import (
    )

    func main() {
    srv := server.NewServer(":8080", "", "", "", nil, nil, 5*time.Second)
    srv := server.NewServer(":8080", "", nil, nil)

    srv.Handler.(*gin.Engine).GET("/", func(c *gin.Context) {
    c.Status(http.StatusOK)
    })

    srv.Start()
    srv.Start("", "", 5*time.Second)
    }
  3. Lajule revised this gist Sep 5, 2022. 1 changed file with 5 additions and 12 deletions.
    17 changes: 5 additions & 12 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -19,13 +19,9 @@ import (

    type Server struct {
    http.Server
    ClientCert string
    ClientKey string
    GracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey string,
    views, assets fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    func NewServer(bindAddr, caCert string, views, assets fs.FS) *Server {
    router := gin.Default()

    if views != nil {
    @@ -88,21 +84,18 @@ func NewServer(bindAddr, caCert, clientCert, clientKey string,
    Handler: router,
    TLSConfig: tlsConfig,
    },
    ClientCert: clientCert,
    ClientKey: clientKey,
    GracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (srv *Server) Start() {
    func (srv *Server) Start(clientCert, clientKey string, gracefulShutdownTimeout time.Duration) {
    go func() {
    var fn string
    var err error

    if len(srv.ClientCert) > 0 && len(srv.ClientKey) > 0 {
    if len(clientCert) > 0 && len(clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = srv.ListenAndServeTLS(srv.ClientCert, srv.ClientKey)
    err = srv.ListenAndServeTLS(clientCert, clientKey)
    } else {
    fn = "ListenAndServe"
    err = srv.ListenAndServe()
    @@ -119,7 +112,7 @@ func (srv *Server) Start() {

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), srv.GracefulShutdownTimeout)
    ctx, cancel := context.WithTimeout(context.Background(), gracefulShutdownTimeout)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
  4. Lajule revised this gist Aug 24, 2022. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,6 @@ package main

    import (
    "net/http"
    "os"
    "time"

    "github.com/gin-gonic/gin"
    @@ -11,7 +10,7 @@ import (
    )

    func main() {
    srv := server.NewServer(":8080", "", "", "", os.DirFS("./assets"), os.DirFS("./views"), 5*time.Second)
    srv := server.NewServer(":8080", "", "", "", nil, nil, 5*time.Second)

    srv.Handler.(*gin.Engine).GET("/", func(c *gin.Context) {
    c.Status(http.StatusOK)
  5. Lajule revised this gist Aug 22, 2022. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -30,6 +30,7 @@ func NewServer(bindAddr, caCert, clientCert, clientKey string,

    if views != nil {
    templates := template.New("")

    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    @@ -61,6 +62,7 @@ func NewServer(bindAddr, caCert, clientCert, clientKey string,
    }

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    @@ -97,13 +99,15 @@ func (srv *Server) Start() {
    go func() {
    var fn string
    var err error

    if len(srv.ClientCert) > 0 && len(srv.ClientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = srv.ListenAndServeTLS(srv.ClientCert, srv.ClientKey)
    } else {
    fn = "ListenAndServe"
    err = srv.ListenAndServe()
    }

    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
  6. Lajule revised this gist Aug 22, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion server.go
    Original file line number Diff line number Diff line change
    @@ -28,8 +28,8 @@ func NewServer(bindAddr, caCert, clientCert, clientKey string,
    views, assets fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if views != nil {
    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
  7. Lajule revised this gist Aug 21, 2022. No changes.
  8. Lajule revised this gist Aug 21, 2022. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion server.go
    Original file line number Diff line number Diff line change
    @@ -56,7 +56,9 @@ func NewServer(bindAddr, caCert, clientCert, clientKey string,
    router.SetHTMLTemplate(templates)
    }

    router.StaticFS("/assets", http.FS(assets))
    if assets != nil {
    router.StaticFS("/assets", http.FS(assets))
    }

    var tlsConfig *tls.Config
    if len(caCert) > 0 {
  9. Lajule revised this gist Aug 19, 2022. 1 changed file with 22 additions and 18 deletions.
    40 changes: 22 additions & 18 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -26,32 +26,36 @@ type Server struct {

    func NewServer(bindAddr, caCert, clientCert, clientKey string,
    views, assets fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }
    if views != nil {
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }
    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }
    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    router.SetHTMLTemplate(templates)
    }

    router := gin.Default()
    router.SetHTMLTemplate(templates)
    router.StaticFS("/assets", http.FS(assets))

    var tlsConfig *tls.Config
  10. Lajule revised this gist Aug 19, 2022. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    package main

    import (
    "net/http"
    "os"
    "time"

    "github.com/gin-gonic/gin"

    "github.com/Lajule/tmp/server"
    )

    func main() {
    srv := server.NewServer(":8080", "", "", "", os.DirFS("./assets"), os.DirFS("./views"), 5*time.Second)

    srv.Handler.(*gin.Engine).GET("/", func(c *gin.Context) {
    c.Status(http.StatusOK)
    })

    srv.Start()
    }
  11. Lajule revised this gist Aug 19, 2022. 1 changed file with 4 additions and 28 deletions.
    32 changes: 4 additions & 28 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,6 @@
    package server

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    @@ -12,13 +11,10 @@ import (
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    @@ -28,10 +24,8 @@ type Server struct {
    GracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    func NewServer(bindAddr, caCert, clientCert, clientKey string,
    views, assets fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    @@ -56,29 +50,11 @@ func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsAr
    log.Fatalf("failed creating views resources: %v", err)
    }

    router := gin.Default()
    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)
    router.StaticFS("/assets", http.FS(assets))

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

  12. Lajule revised this gist Aug 19, 2022. 1 changed file with 13 additions and 13 deletions.
    26 changes: 13 additions & 13 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -23,9 +23,9 @@ import (

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    ClientCert string
    ClientKey string
    GracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    @@ -101,26 +101,26 @@ func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsAr
    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    Handler: router,
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    ClientCert: clientCert,
    ClientKey: clientKey,
    GracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    func (srv *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    if len(srv.ClientCert) > 0 && len(srv.ClientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    err = srv.ListenAndServeTLS(srv.ClientCert, srv.ClientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    err = srv.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    @@ -133,9 +133,9 @@ func (s *Server) Start() {

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    ctx, cancel := context.WithTimeout(context.Background(), srv.GracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    if err := srv.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

  13. Lajule revised this gist Aug 18, 2022. 1 changed file with 0 additions and 286 deletions.
    286 changes: 0 additions & 286 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -111,292 +111,6 @@ func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsAr

    }

    func (s *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
    }()package main

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "html/template"
    "io/fs"
    "log"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    data, err := os.ReadFile(caCert)
    if err != nil {
    log.Fatalf("failed opening trusted SSL Certificate Authorities file: %v", err)
    }

    if !pool.AppendCertsFromPEM(data) {
    log.Fatal("failed parsing CA certificate")
    }

    tlsConfig = &tls.Config{
    RootCAs: pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    MinVersion: tls.VersionTLS12,
    }
    }

    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

    log.Println("server exiting...")
    }


    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

    log.Println("server exiting...")
    }
    ain

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "html/template"
    "io/fs"
    "log"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    data, err := os.ReadFile(caCert)
    if err != nil {
    log.Fatalf("failed opening trusted SSL Certificate Authorities file: %v", err)
    }

    if !pool.AppendCertsFromPEM(data) {
    log.Fatal("failed parsing CA certificate")
    }

    tlsConfig = &tls.Config{
    RootCAs: pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    MinVersion: tls.VersionTLS12,
    }
    }

    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    go func() {
    var fn string
  14. Lajule created this gist Aug 18, 2022.
    429 changes: 429 additions & 0 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,429 @@
    package server

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "html/template"
    "io/fs"
    "log"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    data, err := os.ReadFile(caCert)
    if err != nil {
    log.Fatalf("failed opening trusted SSL Certificate Authorities file: %v", err)
    }

    if !pool.AppendCertsFromPEM(data) {
    log.Fatal("failed parsing CA certificate")
    }

    tlsConfig = &tls.Config{
    RootCAs: pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    MinVersion: tls.VersionTLS12,
    }
    }

    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
    }()package main

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "html/template"
    "io/fs"
    "log"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    data, err := os.ReadFile(caCert)
    if err != nil {
    log.Fatalf("failed opening trusted SSL Certificate Authorities file: %v", err)
    }

    if !pool.AppendCertsFromPEM(data) {
    log.Fatal("failed parsing CA certificate")
    }

    tlsConfig = &tls.Config{
    RootCAs: pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    MinVersion: tls.VersionTLS12,
    }
    }

    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

    log.Println("server exiting...")
    }


    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

    log.Println("server exiting...")
    }
    ain

    import (
    "archive/zip"
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "html/template"
    "io/fs"
    "log"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/spf13/afero"
    "github.com/spf13/afero/zipfs"
    )

    type Server struct {
    http.Server
    clientCert string
    clientKey string
    gracefulShutdownTimeout time.Duration
    }

    func NewServer(bindAddr, caCert, clientCert, clientKey, assetsBasePath, assetsArchiveFile string,
    views fs.FS, gracefulShutdownTimeout time.Duration) *Server {
    router := gin.Default()

    templates := template.New("")
    if err := fs.WalkDir(views, ".", func(path string, info fs.DirEntry, e error) error {
    if e != nil {
    return fmt.Errorf("failed accessing path: %w", e)
    }

    if info.IsDir() {
    return nil
    }

    file, err := fs.ReadFile(views, path)
    if err != nil {
    return fmt.Errorf("failed reading file: %w", err)
    }

    if _, err := templates.Parse(string(file)); err != nil {
    return fmt.Errorf("failed parsing template: %w", err)
    }

    return nil
    }); err != nil {
    log.Fatalf("failed creating views resources: %v", err)
    }

    router.SetHTMLTemplate(templates)

    var fs afero.Fs
    if len(assetsArchiveFile) > 0 {
    if filepath.Ext(assetsArchiveFile) != ".zip" {
    log.Fatalf("unsupported archive file: %v", assetsArchiveFile)
    }

    zr, err := zip.OpenReader(assetsArchiveFile)
    if err != nil {
    log.Fatalf("failed opening archive: %v", err)
    }

    fs = zipfs.New(&zr.Reader)
    } else {
    fs = afero.NewOsFs()
    }

    staticFS := afero.NewHttpFs(afero.NewBasePathFs(fs, assetsBasePath))
    router.StaticFS("/assets", staticFS)

    var tlsConfig *tls.Config

    if len(caCert) > 0 {
    pool := x509.NewCertPool()

    data, err := os.ReadFile(caCert)
    if err != nil {
    log.Fatalf("failed opening trusted SSL Certificate Authorities file: %v", err)
    }

    if !pool.AppendCertsFromPEM(data) {
    log.Fatal("failed parsing CA certificate")
    }

    tlsConfig = &tls.Config{
    RootCAs: pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    MinVersion: tls.VersionTLS12,
    }
    }

    return &Server{
    Server: http.Server{
    Addr: bindAddr,
    Handler: gin.Default(),
    TLSConfig: tlsConfig,
    },
    clientCert: clientCert,
    clientKey: clientKey,
    gracefulShutdownTimeout: gracefulShutdownTimeout,
    }

    }

    func (s *Server) Start() {
    go func() {
    var fn string
    var err error
    if len(s.clientCert) > 0 && len(s.clientKey) > 0 {
    fn = "ListenAndServeTLS"
    err = s.ListenAndServeTLS(s.clientCert, s.clientKey)
    } else {
    fn = "ListenAndServe"
    err = s.ListenAndServe()
    }
    if err != http.ErrServerClosed {
    log.Fatalf("failed listenning and serving (%v): %v", fn, err)
    }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), s.gracefulShutdownTimeout)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
    log.Fatalf("server forced to shutdown: %v", err)
    }

    log.Println("server exiting...")
    }