github.com/drone/runner-go@v1.12.0/server/server.go (about)

     1  // Copyright 2019 Drone.IO Inc. All rights reserved.
     2  // Use of this source code is governed by the Polyform License
     3  // that can be found in the LICENSE file.
     4  
     5  // Package server provides an HTTP server with support for TLS
     6  // and graceful shutdown.
     7  package server
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"net/http"
    13  	"os"
    14  	"path/filepath"
    15  
    16  	"golang.org/x/crypto/acme/autocert"
    17  	"golang.org/x/sync/errgroup"
    18  )
    19  
    20  // A Server defines parameters for running an HTTP server.
    21  type Server struct {
    22  	Acme    bool
    23  	Addr    string
    24  	Cert    string
    25  	Key     string
    26  	Host    string
    27  	Handler http.Handler
    28  }
    29  
    30  // ListenAndServe initializes a server to respond to HTTP network requests.
    31  func (s Server) ListenAndServe(ctx context.Context) error {
    32  	if s.Acme {
    33  		return s.listenAndServeAcme(ctx)
    34  	} else if s.Key != "" {
    35  		return s.listenAndServeTLS(ctx)
    36  	}
    37  	return s.listenAndServe(ctx)
    38  }
    39  
    40  func (s Server) listenAndServe(ctx context.Context) error {
    41  	var g errgroup.Group
    42  	s1 := &http.Server{
    43  		Addr:    s.Addr,
    44  		Handler: s.Handler,
    45  	}
    46  	g.Go(func() error {
    47  		<-ctx.Done()
    48  		return s1.Shutdown(context.Background())
    49  	})
    50  	g.Go(func() error {
    51  		return s1.ListenAndServe()
    52  	})
    53  	return g.Wait()
    54  }
    55  
    56  func (s Server) listenAndServeTLS(ctx context.Context) error {
    57  	var g errgroup.Group
    58  	s1 := &http.Server{
    59  		Addr:    ":http",
    60  		Handler: s.Handler,
    61  	}
    62  	s2 := &http.Server{
    63  		Addr:    ":https",
    64  		Handler: s.Handler,
    65  	}
    66  	g.Go(func() error {
    67  		return s1.ListenAndServe()
    68  	})
    69  	g.Go(func() error {
    70  		return s2.ListenAndServeTLS(
    71  			s.Cert,
    72  			s.Key,
    73  		)
    74  	})
    75  	g.Go(func() error {
    76  		<-ctx.Done()
    77  
    78  		var gShutdown errgroup.Group
    79  
    80  		gShutdown.Go(func() error {
    81  			return s1.Shutdown(context.Background())
    82  		})
    83  		gShutdown.Go(func() error {
    84  			return s2.Shutdown(context.Background())
    85  		})
    86  
    87  		return gShutdown.Wait()
    88  	})
    89  	return g.Wait()
    90  }
    91  
    92  func (s Server) listenAndServeAcme(ctx context.Context) error {
    93  	var g errgroup.Group
    94  
    95  	c := cacheDir()
    96  	m := &autocert.Manager{
    97  		Cache:      autocert.DirCache(c),
    98  		Prompt:     autocert.AcceptTOS,
    99  		HostPolicy: autocert.HostWhitelist(s.Host),
   100  	}
   101  	s1 := &http.Server{
   102  		Addr:    ":http",
   103  		Handler: m.HTTPHandler(s.Handler),
   104  	}
   105  	s2 := &http.Server{
   106  		Addr:    ":https",
   107  		Handler: s.Handler,
   108  		TLSConfig: &tls.Config{
   109  			GetCertificate: m.GetCertificate,
   110  			NextProtos:     []string{"h2", "http/1.1"},
   111  			MinVersion:     tls.VersionTLS12,
   112  		},
   113  	}
   114  	g.Go(func() error {
   115  		return s1.ListenAndServe()
   116  	})
   117  	g.Go(func() error {
   118  		return s2.ListenAndServeTLS("", "")
   119  	})
   120  	g.Go(func() error {
   121  		<-ctx.Done()
   122  
   123  		var gShutdown errgroup.Group
   124  
   125  		gShutdown.Go(func() error {
   126  			return s1.Shutdown(context.Background())
   127  		})
   128  		gShutdown.Go(func() error {
   129  			return s2.Shutdown(context.Background())
   130  		})
   131  
   132  		return gShutdown.Wait()
   133  	})
   134  	return g.Wait()
   135  }
   136  
   137  func cacheDir() string {
   138  	const base = "golang-autocert"
   139  	if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
   140  		return filepath.Join(xdg, base)
   141  	}
   142  	return filepath.Join(os.Getenv("HOME"), ".cache", base)
   143  }