github.com/prebid/prebid-server/v2@v2.18.0/server/server.go (about) 1 package server 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "net/http" 8 "os" 9 "os/signal" 10 "strconv" 11 "syscall" 12 "time" 13 14 "github.com/NYTimes/gziphandler" 15 "github.com/golang/glog" 16 "github.com/prebid/prebid-server/v2/config" 17 "github.com/prebid/prebid-server/v2/metrics" 18 metricsconfig "github.com/prebid/prebid-server/v2/metrics/config" 19 ) 20 21 // Listen blocks forever, serving PBS requests on the given port. This will block forever, until the process is shut down. 22 func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.Handler, metrics *metricsconfig.DetailedMetricsEngine) (err error) { 23 stopSignals := make(chan os.Signal, 1) 24 signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT) 25 26 // Run the servers. Fan any process-stopper signals out to each server for graceful shutdowns. 27 stopAdmin := make(chan os.Signal) 28 stopMain := make(chan os.Signal) 29 stopPrometheus := make(chan os.Signal) 30 done := make(chan struct{}) 31 32 if cfg.UnixSocketEnable && len(cfg.UnixSocketName) > 0 { // start the unix_socket server if config enable-it. 33 var ( 34 socketListener net.Listener 35 mainServer = newSocketServer(cfg, handler) 36 ) 37 go shutdownAfterSignals(mainServer, stopMain, done) 38 if socketListener, err = newUnixListener(mainServer.Addr, metrics); err != nil { 39 glog.Errorf("Error listening for Unix-Socket connections on path %s: %v for socket server", mainServer.Addr, err) 40 return 41 } 42 go runServer(mainServer, "UnixSocket", socketListener) 43 } else { // start the TCP server 44 var ( 45 mainListener net.Listener 46 mainServer = newMainServer(cfg, handler) 47 ) 48 go shutdownAfterSignals(mainServer, stopMain, done) 49 if mainListener, err = newTCPListener(mainServer.Addr, metrics); err != nil { 50 glog.Errorf("Error listening for TCP connections on %s: %v for main server", mainServer.Addr, err) 51 return 52 } 53 go runServer(mainServer, "Main", mainListener) 54 } 55 56 if cfg.Admin.Enabled { 57 adminServer := newAdminServer(cfg, adminHandler) 58 go shutdownAfterSignals(adminServer, stopAdmin, done) 59 60 var adminListener net.Listener 61 if adminListener, err = newTCPListener(adminServer.Addr, nil); err != nil { 62 glog.Errorf("Error listening for TCP connections on %s: %v for admin server", adminServer.Addr, err) 63 return 64 } 65 go runServer(adminServer, "Admin", adminListener) 66 } 67 68 if cfg.Metrics.Prometheus.Port != 0 { 69 var ( 70 prometheusListener net.Listener 71 prometheusServer = newPrometheusServer(cfg, metrics) 72 ) 73 go shutdownAfterSignals(prometheusServer, stopPrometheus, done) 74 if prometheusListener, err = newTCPListener(prometheusServer.Addr, nil); err != nil { 75 glog.Errorf("Error listening for TCP connections on %s: %v for prometheus server", prometheusServer.Addr, err) 76 return 77 } 78 79 go runServer(prometheusServer, "Prometheus", prometheusListener) 80 wait(stopSignals, done, stopMain, stopAdmin, stopPrometheus) 81 } else { 82 wait(stopSignals, done, stopMain, stopAdmin) 83 } 84 85 return 86 } 87 88 func newAdminServer(cfg *config.Configuration, handler http.Handler) *http.Server { 89 return &http.Server{ 90 Addr: cfg.Host + ":" + strconv.Itoa(cfg.AdminPort), 91 Handler: handler, 92 } 93 } 94 95 func newMainServer(cfg *config.Configuration, handler http.Handler) *http.Server { 96 serverHandler := getCompressionEnabledHandler(handler, cfg.Compression.Response) 97 98 return &http.Server{ 99 Addr: cfg.Host + ":" + strconv.Itoa(cfg.Port), 100 Handler: serverHandler, 101 ReadTimeout: 15 * time.Second, 102 WriteTimeout: 15 * time.Second, 103 } 104 105 } 106 107 func newSocketServer(cfg *config.Configuration, handler http.Handler) *http.Server { 108 serverHandler := getCompressionEnabledHandler(handler, cfg.Compression.Response) 109 110 return &http.Server{ 111 Addr: cfg.UnixSocketName, 112 Handler: serverHandler, 113 ReadTimeout: 15 * time.Second, 114 WriteTimeout: 15 * time.Second, 115 } 116 } 117 118 func getCompressionEnabledHandler(h http.Handler, compressionInfo config.CompressionInfo) http.Handler { 119 if compressionInfo.GZIP { 120 h = gziphandler.GzipHandler(h) 121 } 122 return h 123 } 124 125 func runServer(server *http.Server, name string, listener net.Listener) (err error) { 126 if server == nil { 127 err = fmt.Errorf(">> Server is a nil_ptr.") 128 glog.Errorf("%s server quit with error: %v", name, err) 129 return 130 } else if listener == nil { 131 err = fmt.Errorf(">> Listener is a nil.") 132 glog.Errorf("%s server quit with error: %v", name, err) 133 return 134 } 135 136 glog.Infof("%s server starting on: %s", name, server.Addr) 137 if err = server.Serve(listener); err != nil { 138 glog.Errorf("%s server quit with error: %v", name, err) 139 } 140 return 141 } 142 143 func newTCPListener(address string, metrics metrics.MetricsEngine) (net.Listener, error) { 144 ln, err := net.Listen("tcp", address) 145 if err != nil { 146 return nil, fmt.Errorf("Error listening for TCP connections on %s: %v", address, err) 147 } 148 149 // This cast is in Go's core libs as Server.ListenAndServe(), so it _should_ be safe, but just in case it changes in a future version... 150 if casted, ok := ln.(*net.TCPListener); ok { 151 ln = &tcpKeepAliveListener{casted} 152 } else { 153 glog.Warning("net.Listen(\"tcp\", \"addr\") didn't return a TCPListener as it did in Go 1.9. Things will probably work fine... but this should be investigated.") 154 } 155 156 if metrics != nil { 157 ln = &monitorableListener{ln, metrics} 158 } 159 160 return ln, nil 161 } 162 163 func newUnixListener(address string, metrics metrics.MetricsEngine) (net.Listener, error) { 164 ln, err := net.Listen("unix", address) 165 if err != nil { 166 return nil, fmt.Errorf("Error listening for Unix-Socket connections on path %s: %v", address, err) 167 } 168 169 if casted, ok := ln.(*net.UnixListener); ok { 170 ln = &unixListener{casted} 171 } else { 172 glog.Warning("net.Listen(\"unix\", \"addr\") didn't return an UnixListener.") 173 } 174 175 if metrics != nil { 176 ln = &monitorableListener{ln, metrics} 177 } 178 179 return ln, nil 180 } 181 182 func wait(inbound <-chan os.Signal, done <-chan struct{}, outbound ...chan<- os.Signal) { 183 sig := <-inbound 184 185 for i := 0; i < len(outbound); i++ { 186 go sendSignal(outbound[i], sig) 187 } 188 189 for i := 0; i < len(outbound); i++ { 190 <-done 191 } 192 } 193 194 func shutdownAfterSignals(server *http.Server, stopper <-chan os.Signal, done chan<- struct{}) { 195 sig := <-stopper 196 197 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 198 defer cancel() 199 200 var s struct{} 201 glog.Infof("Stopping %s because of signal: %s", server.Addr, sig.String()) 202 if err := server.Shutdown(ctx); err != nil { 203 glog.Errorf("Failed to shutdown %s: %v", server.Addr, err) 204 } 205 done <- s 206 } 207 208 func sendSignal(to chan<- os.Signal, sig os.Signal) { 209 to <- sig 210 }