Example #1
0
// TestServer returns a test server.
// The ports can be retreived with server.LocalAddr(). The testserver itself can be stopped
// with Stop(). It just takes a normal Corefile as input.
func TestServer(t *testing.T, corefile string) (*server.Server, error) {

	crfile := CorefileInput{Contents: []byte(corefile)}
	configs, err := loadConfigs(path.Base(crfile.Path()), bytes.NewReader(crfile.Body()))
	if err != nil {
		return nil, err
	}
	groupings, err := arrangeBindings(configs)
	if err != nil {
		return nil, err
	}
	t.Logf("Starting %d servers", len(groupings))

	group := groupings[0]
	s, err := server.New(group.BindAddr.String(), group.Configs, time.Second)
	return s, err
}
Example #2
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 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
		}
		// TODO(miek): does not work, because this callback uses http instead of dns
		//		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 net.Listener
			pc net.PacketConn
		)

		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["tcp"+s.Addr]; ok {
				file := os.NewFile(fdIndex, "")

				fln, err := net.FileListener(file)
				if err != nil {
					return err
				}

				ln, ok = fln.(*net.TCPListener)
				if !ok {
					return errors.New("listener for " + s.Addr + " was not a *net.TCPListener")
				}

				file.Close()
				delete(loadedGob.ListenerFds, "tcp"+s.Addr)
			}
			if fdIndex, ok := loadedGob.ListenerFds["udp"+s.Addr]; ok {
				file := os.NewFile(fdIndex, "")

				fpc, err := net.FilePacketConn(file)
				if err != nil {
					return err
				}

				pc, ok = fpc.(*net.UDPConn)
				if !ok {
					return errors.New("packetConn for " + s.Addr + " was not a *net.PacketConn")
				}

				file.Close()
				delete(loadedGob.ListenerFds, "udp"+s.Addr)
			}
		}

		wg.Add(1)
		go func(s *server.Server, ln net.Listener, pc net.PacketConn) {
			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 && pc != nil {
				errChan <- s.Serve(ln, pc)
			} else {
				errChan <- s.ListenAndServe()
			}
		}(s, ln, pc)

		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
}