// startServers starts all the servers in groupings, // taking into account whether or not this process is // a child from a graceful restart or not. It blocks // until the servers are listening. func startServers(groupings bindingGroup) error { var startupWg sync.WaitGroup errChan := make(chan error, len(groupings)) // must be buffered to allow Serve functions below to return if stopped later for _, group := range groupings { s, err := server.New(group.BindAddr.String(), group.Configs, GracefulTimeout) if err != nil { return err } s.HTTP2 = HTTP2 // TODO: This setting is temporary var ln server.ListenerFile if IsRestart() { // Look up this server's listener in the map of inherited file descriptors; // if we don't have one, we must make a new one (later). if fdIndex, ok := loadedGob.ListenerFds[s.Addr]; ok { file := os.NewFile(fdIndex, "") fln, err := net.FileListener(file) if err != nil { return err } ln, ok = fln.(server.ListenerFile) if !ok { return errors.New("listener for " + s.Addr + " was not a ListenerFile") } file.Close() delete(loadedGob.ListenerFds, s.Addr) } } wg.Add(1) go func(s *server.Server, ln server.ListenerFile) { defer wg.Done() // run startup functions that should only execute when // the original parent process is starting. if !IsRestart() && !startedBefore { err := s.RunFirstStartupFuncs() if err != nil { errChan <- err return } } // start the server if ln != nil { errChan <- s.Serve(ln) } else { errChan <- s.ListenAndServe() } }(s, ln) startupWg.Add(1) go func(s *server.Server) { defer startupWg.Done() s.WaitUntilStarted() }(s) serversMu.Lock() servers = append(servers, s) serversMu.Unlock() } // Close the remaining (unused) file descriptors to free up resources if IsRestart() { for key, fdIndex := range loadedGob.ListenerFds { os.NewFile(fdIndex, "").Close() delete(loadedGob.ListenerFds, key) } } // Wait for all servers to finish starting startupWg.Wait() // Return the first error, if any select { case err := <-errChan: // "use of closed network connection" is normal if it was a graceful shutdown if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { return err } default: } return nil }
func main() { flag.Parse() if version { fmt.Printf("%s %s\n", app.Name, app.Version) os.Exit(0) } // Set CPU cap err := app.SetCPU(cpu) if err != nil { log.Fatal(err) } // Load config from file addresses, err := loadConfigs() if err != nil { log.Fatal(err) } // Start each server with its one or more configurations for addr, configs := range addresses { s, err := server.New(addr.String(), configs) if err != nil { log.Fatal(err) } s.HTTP2 = app.HTTP2 // TODO: This setting is temporary app.Wg.Add(1) go func(s *server.Server) { defer app.Wg.Done() err := s.Serve() if err != nil { log.Fatal(err) // kill whole process to avoid a half-alive zombie server } }(s) app.Servers = append(app.Servers, s) } // Show initialization output if !app.Quiet { var checkedFdLimit bool for addr, configs := range addresses { for _, conf := range configs { // Print address of site fmt.Println(conf.Address()) // Note if non-localhost site resolves to loopback interface if addr.IP.IsLoopback() && !isLocalhost(conf.Host) { fmt.Printf("Notice: %s is only accessible on this machine (%s)\n", conf.Host, addr.IP.String()) } if !checkedFdLimit && !addr.IP.IsLoopback() && !isLocalhost(conf.Host) { checkFdlimit() checkedFdLimit = true } } } } // Wait for all listeners to stop app.Wg.Wait() }
// startServers starts all the servers in groupings, // taking into account whether or not this process is // a child from a graceful restart or not. It blocks // until the servers are listening. func startServers(groupings Group) error { var startupWg sync.WaitGroup errChan := make(chan error) for _, group := range groupings { s, err := server.New(group.BindAddr.String(), group.Configs) if err != nil { return err } s.HTTP2 = HTTP2 // TODO: This setting is temporary var ln server.ListenerFile if IsRestart() { // Look up this server's listener in the map of inherited file descriptors; // if we don't have one, we must make a new one (later). if fdIndex, ok := loadedGob.ListenerFds[s.Addr]; ok { file := os.NewFile(fdIndex, "") fln, err := net.FileListener(file) if err != nil { return err } ln, ok = fln.(server.ListenerFile) if !ok { return errors.New("listener for " + s.Addr + " was not a ListenerFile") } delete(loadedGob.ListenerFds, s.Addr) // mark it as used } } wg.Add(1) go func(s *server.Server, ln server.ListenerFile) { defer wg.Done() if ln != nil { errChan <- s.Serve(ln) } else { errChan <- s.ListenAndServe() } }(s, ln) startupWg.Add(1) go func(s *server.Server) { defer startupWg.Done() s.WaitUntilStarted() }(s) serversMu.Lock() servers = append(servers, s) serversMu.Unlock() } // Wait for all servers to finish starting startupWg.Wait() // Return the first error, if any select { case err := <-errChan: // "use of closed network connection" is normal if it was a graceful shutdown if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { return err } default: } return nil }
// startServers starts all the servers in groupings, // taking into account whether or not this process is // from a graceful restart or not. It blocks until // the servers are listening. func startServers(groupings bindingGroup) error { var startupWg sync.WaitGroup errChan := make(chan error, len(groupings)) // must be buffered to allow Serve functions below to return if stopped later for _, group := range groupings { s, err := server.New(group.BindAddr.String(), group.Configs, GracefulTimeout) if err != nil { return err } s.HTTP2 = HTTP2 s.ReqCallback = https.RequestCallback // ensures we can solve ACME challenges while running if s.OnDemandTLS { s.TLSConfig.GetCertificate = https.GetOrObtainCertificate // TLS on demand -- awesome! } else { s.TLSConfig.GetCertificate = https.GetCertificate } var ln server.ListenerFile if len(restartFds) > 0 { // Reuse the listeners for in-process restart if file, ok := restartFds[s.Addr]; ok { fln, err := net.FileListener(file) if err != nil { return err } ln, ok = fln.(server.ListenerFile) if !ok { return errors.New("listener for " + s.Addr + " was not a ListenerFile") } file.Close() delete(restartFds, s.Addr) } } wg.Add(1) go func(s *server.Server, ln server.ListenerFile) { defer wg.Done() // run startup functions that should only execute when // the original parent process is starting. if !startedBefore { err := s.RunFirstStartupFuncs() if err != nil { errChan <- err return } } // start the server if ln != nil { errChan <- s.Serve(ln) } else { errChan <- s.ListenAndServe() } }(s, ln) startupWg.Add(1) go func(s *server.Server) { defer startupWg.Done() s.WaitUntilStarted() }(s) serversMu.Lock() servers = append(servers, s) serversMu.Unlock() } // Close the remaining (unused) file descriptors to free up resources if len(restartFds) > 0 { for key, file := range restartFds { file.Close() delete(restartFds, key) } } // Wait for all servers to finish starting startupWg.Wait() // Return the first error, if any select { case err := <-errChan: // "use of closed network connection" is normal if it was a graceful shutdown if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { return err } default: } return nil }