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  }