github.com/prebid/prebid-server@v0.275.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/config" 17 "github.com/prebid/prebid-server/metrics" 18 metricsconfig "github.com/prebid/prebid-server/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 adminServer := newAdminServer(cfg, adminHandler) 33 go shutdownAfterSignals(adminServer, stopAdmin, done) 34 35 if cfg.UnixSocketEnable && len(cfg.UnixSocketName) > 0 { // start the unix_socket server if config enable-it. 36 var ( 37 socketListener net.Listener 38 mainServer = newSocketServer(cfg, handler) 39 ) 40 go shutdownAfterSignals(mainServer, stopMain, done) 41 if socketListener, err = newUnixListener(mainServer.Addr, metrics); err != nil { 42 glog.Errorf("Error listening for Unix-Socket connections on path %s: %v for socket server", mainServer.Addr, err) 43 return 44 } 45 go runServer(mainServer, "UnixSocket", socketListener) 46 } else { // start the TCP server 47 var ( 48 mainListener net.Listener 49 mainServer = newMainServer(cfg, handler) 50 ) 51 go shutdownAfterSignals(mainServer, stopMain, done) 52 if mainListener, err = newTCPListener(mainServer.Addr, metrics); err != nil { 53 glog.Errorf("Error listening for TCP connections on %s: %v for main server", mainServer.Addr, err) 54 return 55 } 56 go runServer(mainServer, "Main", mainListener) 57 } 58 59 var adminListener net.Listener 60 if adminListener, err = newTCPListener(adminServer.Addr, nil); err != nil { 61 glog.Errorf("Error listening for TCP connections on %s: %v for admin server", adminServer.Addr, err) 62 return 63 } 64 go runServer(adminServer, "Admin", adminListener) 65 66 if cfg.Metrics.Prometheus.Port != 0 { 67 var ( 68 prometheusListener net.Listener 69 prometheusServer = newPrometheusServer(cfg, metrics) 70 ) 71 go shutdownAfterSignals(prometheusServer, stopPrometheus, done) 72 if prometheusListener, err = newTCPListener(prometheusServer.Addr, nil); err != nil { 73 glog.Errorf("Error listening for TCP connections on %s: %v for prometheus server", adminServer.Addr, err) 74 return 75 } 76 77 go runServer(prometheusServer, "Prometheus", prometheusListener) 78 wait(stopSignals, done, stopMain, stopAdmin, stopPrometheus) 79 } else { 80 wait(stopSignals, done, stopMain, stopAdmin) 81 } 82 83 return 84 } 85 86 func newAdminServer(cfg *config.Configuration, handler http.Handler) *http.Server { 87 return &http.Server{ 88 Addr: cfg.Host + ":" + strconv.Itoa(cfg.AdminPort), 89 Handler: handler, 90 } 91 } 92 93 func newMainServer(cfg *config.Configuration, handler http.Handler) *http.Server { 94 serverHandler := getCompressionEnabledHandler(handler, cfg.Compression.Response) 95 96 return &http.Server{ 97 Addr: cfg.Host + ":" + strconv.Itoa(cfg.Port), 98 Handler: serverHandler, 99 ReadTimeout: 15 * time.Second, 100 WriteTimeout: 15 * time.Second, 101 } 102 103 } 104 105 func newSocketServer(cfg *config.Configuration, handler http.Handler) *http.Server { 106 serverHandler := getCompressionEnabledHandler(handler, cfg.Compression.Response) 107 108 return &http.Server{ 109 Addr: cfg.UnixSocketName, 110 Handler: serverHandler, 111 ReadTimeout: 15 * time.Second, 112 WriteTimeout: 15 * time.Second, 113 } 114 } 115 116 func getCompressionEnabledHandler(h http.Handler, compressionInfo config.CompressionInfo) http.Handler { 117 if compressionInfo.GZIP { 118 h = gziphandler.GzipHandler(h) 119 } 120 return h 121 } 122 123 func runServer(server *http.Server, name string, listener net.Listener) (err error) { 124 if server == nil { 125 err = fmt.Errorf(">> Server is a nil_ptr.") 126 glog.Errorf("%s server quit with error: %v", name, err) 127 return 128 } else if listener == nil { 129 err = fmt.Errorf(">> Listener is a nil.") 130 glog.Errorf("%s server quit with error: %v", name, err) 131 return 132 } 133 134 glog.Infof("%s server starting on: %s", name, server.Addr) 135 if err = server.Serve(listener); err != nil { 136 glog.Errorf("%s server quit with error: %v", name, err) 137 } 138 return 139 } 140 141 func newTCPListener(address string, metrics metrics.MetricsEngine) (net.Listener, error) { 142 ln, err := net.Listen("tcp", address) 143 if err != nil { 144 return nil, fmt.Errorf("Error listening for TCP connections on %s: %v", address, err) 145 } 146 147 // 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... 148 if casted, ok := ln.(*net.TCPListener); ok { 149 ln = &tcpKeepAliveListener{casted} 150 } else { 151 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.") 152 } 153 154 if metrics != nil { 155 ln = &monitorableListener{ln, metrics} 156 } 157 158 return ln, nil 159 } 160 161 func newUnixListener(address string, metrics metrics.MetricsEngine) (net.Listener, error) { 162 ln, err := net.Listen("unix", address) 163 if err != nil { 164 return nil, fmt.Errorf("Error listening for Unix-Socket connections on path %s: %v", address, err) 165 } 166 167 if casted, ok := ln.(*net.UnixListener); ok { 168 ln = &unixListener{casted} 169 } else { 170 glog.Warning("net.Listen(\"unix\", \"addr\") didn't return an UnixListener.") 171 } 172 173 if metrics != nil { 174 ln = &monitorableListener{ln, metrics} 175 } 176 177 return ln, nil 178 } 179 180 func wait(inbound <-chan os.Signal, done <-chan struct{}, outbound ...chan<- os.Signal) { 181 sig := <-inbound 182 183 for i := 0; i < len(outbound); i++ { 184 go sendSignal(outbound[i], sig) 185 } 186 187 for i := 0; i < len(outbound); i++ { 188 <-done 189 } 190 } 191 192 func shutdownAfterSignals(server *http.Server, stopper <-chan os.Signal, done chan<- struct{}) { 193 sig := <-stopper 194 195 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 196 defer cancel() 197 198 var s struct{} 199 glog.Infof("Stopping %s because of signal: %s", server.Addr, sig.String()) 200 if err := server.Shutdown(ctx); err != nil { 201 glog.Errorf("Failed to shutdown %s: %v", server.Addr, err) 202 } 203 done <- s 204 } 205 206 func sendSignal(to chan<- os.Signal, sig os.Signal) { 207 to <- sig 208 }