Example #1
0
File: caddy.go Project: kavun/caddy
// 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
}
Example #2
0
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()
}
Example #3
0
// 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
}
Example #4
0
// 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
}