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 }