aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/core.go (renamed from core/caddy.go)73
-rw-r--r--core/restart.go9
-rw-r--r--server/config.go3
-rw-r--r--server/graceful.go76
-rw-r--r--server/server.go64
5 files changed, 93 insertions, 132 deletions
diff --git a/core/caddy.go b/core/core.go
index 30038d28f..1c0eba49a 100644
--- a/core/caddy.go
+++ b/core/core.go
@@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"log"
+ "net"
"os"
"path"
"strings"
@@ -197,36 +198,52 @@ func startServers(groupings bindingGroup) error {
s.TLSConfig.GetCertificate = https.GetCertificate
}
- 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)
+ 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 server.ListenerFile) {
+ 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.
+ // run startup functions that should only execute when the original parent process is starting.
if !IsRestart() && !startedBefore {
err := s.RunFirstStartupFuncs()
if err != nil {
@@ -236,14 +253,12 @@ func startServers(groupings bindingGroup) error {
}
// start the server
- // TODO(miek): for now will always be nil, so we will run ListenAndServe()
- // TODO(miek): this is also why graceful restarts don't work.
- if ln != nil {
- //errChan <- s.Serve(ln)
+ if ln != nil && pc != nil {
+ errChan <- s.Serve(ln, pc)
} else {
errChan <- s.ListenAndServe()
}
- }(s, ln)
+ }(s, ln, pc)
startupWg.Add(1)
go func(s *server.Server) {
diff --git a/core/restart.go b/core/restart.go
index c13a8fe7c..1c053c13c 100644
--- a/core/restart.go
+++ b/core/restart.go
@@ -81,9 +81,14 @@ func Restart(newCorefile Input) error {
// Add file descriptors of all the sockets
serversMu.Lock()
- for i, s := range servers {
+ j := 0
+ for _, s := range servers {
extraFiles = append(extraFiles, s.ListenerFd())
- crfileGob.ListenerFds[s.Addr] = uintptr(4 + i) // 4 fds come before any of the listeners
+ extraFiles = append(extraFiles, s.PacketConnFd())
+ // So this will be 0 1 2 3 TCP UDP TCP UDP ... etc.
+ crfileGob.ListenerFds["tcp"+s.Addr] = uintptr(4 + j) // 4 fds come before any of the listeners
+ crfileGob.ListenerFds["udp"+s.Addr] = uintptr(4 + j + 1) // add udp after that
+ j += 2
}
serversMu.Unlock()
diff --git a/server/config.go b/server/config.go
index 79a9ba84d..de64ed7f2 100644
--- a/server/config.go
+++ b/server/config.go
@@ -42,7 +42,8 @@ type Config struct {
FirstStartup []func() error
// Functions (or methods) to execute when the server quits;
- // these are executed in response to SIGINT and are blocking
+ // these are executed in response to SIGINT and are blocking. These
+ // function are *also* called when we are restarting.
Shutdown []func() error
// The path to the configuration file from which this was loaded
diff --git a/server/graceful.go b/server/graceful.go
deleted file mode 100644
index 5057d039b..000000000
--- a/server/graceful.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package server
-
-import (
- "net"
- "sync"
- "syscall"
-)
-
-// newGracefulListener returns a gracefulListener that wraps l and
-// uses wg (stored in the host server) to count connections.
-func newGracefulListener(l ListenerFile, wg *sync.WaitGroup) *gracefulListener {
- gl := &gracefulListener{ListenerFile: l, stop: make(chan error), httpWg: wg}
- go func() {
- <-gl.stop
- gl.Lock()
- gl.stopped = true
- gl.Unlock()
- gl.stop <- gl.ListenerFile.Close()
- }()
- return gl
-}
-
-// gracefuListener is a net.Listener which can
-// count the number of connections on it. Its
-// methods mainly wrap net.Listener to be graceful.
-type gracefulListener struct {
- ListenerFile
- stop chan error
- stopped bool
- sync.Mutex // protects the stopped flag
- httpWg *sync.WaitGroup // pointer to the host's wg used for counting connections
-}
-
-// Accept accepts a connection.
-func (gl *gracefulListener) Accept() (c net.Conn, err error) {
- c, err = gl.ListenerFile.Accept()
- if err != nil {
- return
- }
- c = gracefulConn{Conn: c, httpWg: gl.httpWg}
- gl.httpWg.Add(1)
- return
-}
-
-// Close immediately closes the listener.
-func (gl *gracefulListener) Close() error {
- gl.Lock()
- if gl.stopped {
- gl.Unlock()
- return syscall.EINVAL
- }
- gl.Unlock()
- gl.stop <- nil
- return <-gl.stop
-}
-
-// gracefulConn represents a connection on a
-// gracefulListener so that we can keep track
-// of the number of connections, thus facilitating
-// a graceful shutdown.
-type gracefulConn struct {
- net.Conn
- httpWg *sync.WaitGroup // pointer to the host server's connection waitgroup
-}
-
-// Close closes c's underlying connection while updating the wg count.
-func (c gracefulConn) Close() error {
- err := c.Conn.Close()
- if err != nil {
- return err
- }
- // close can fail on http2 connections (as of Oct. 2015, before http2 in std lib)
- // so don't decrement count unless close succeeds
- c.httpWg.Done()
- return nil
-}
diff --git a/server/server.go b/server/server.go
index f3462d2c4..068a54a31 100644
--- a/server/server.go
+++ b/server/server.go
@@ -32,15 +32,15 @@ type Server struct {
Addr string // Address we listen on
mux *dns.ServeMux
server [2]*dns.Server // by convention 0 is tcp and 1 is udp
- tcp net.Listener
- udp net.PacketConn
+
+ tcp net.Listener
+ udp net.PacketConn
+ listenerMu sync.Mutex // protects listener and packetconn
tls bool // whether this server is serving all HTTPS hosts or not
TLSConfig *tls.Config
OnDemandTLS bool // whether this server supports on-demand TLS (load certs at handshake-time)
zones map[string]zone // zones keyed by their address
- listener ListenerFile // the listener which is bound to the socket
- listenerMu sync.Mutex // protects listener
dnsWg sync.WaitGroup // used to wait on outstanding connections
startChan chan struct{} // used to block until server is finished starting
connTimeout time.Duration // the maximum duration of a graceful shutdown
@@ -48,12 +48,6 @@ type Server struct {
SNICallback func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error)
}
-// ListenerFile represents a listener.
-type ListenerFile interface {
- net.Listener
- File() (*os.File, error)
-}
-
// OptionalCallback is a function that may or may not handle a request.
// It returns whether or not it handled the request. If it handled the
// request, it is presumed that no further request handling should occur.
@@ -145,25 +139,31 @@ func (s *Server) LocalAddr() (net.Addr, net.Addr) {
return tcp, udp
}
-// Serve starts the server with an existing listener. It blocks until the
-// server stops.
-/*
-func (s *Server) Serve(ln ListenerFile) error {
- // TODO(miek): Go DNS has no server stuff that allows you to give it a listener
- // and use that.
+// Serve starts the server with an existing listener. It blocks until the server stops.
+func (s *Server) Serve(ln net.Listener, pc net.PacketConn) error {
err := s.setup()
if err != nil {
- defer close(s.startChan) // MUST defer so error is properly reported, same with all cases in this file
+ close(s.startChan) // MUST defer so error is properly reported, same with all cases in this file
return err
}
- return s.serve(ln)
+ s.listenerMu.Lock()
+ s.server[0] = &dns.Server{Listener: ln, Net: "tcp", Handler: s.mux}
+ s.tcp = ln
+ s.server[1] = &dns.Server{PacketConn: pc, Net: "udp", Handler: s.mux}
+ s.udp = pc
+ s.listenerMu.Unlock()
+
+ go func() {
+ s.server[0].ActivateAndServe()
+ }()
+ close(s.startChan)
+ return s.server[1].ActivateAndServe()
}
-*/
// ListenAndServe starts the server with a new listener. It blocks until the server stops.
func (s *Server) ListenAndServe() error {
err := s.setup()
- // defer close(s.startChan) // Don't understand why defer wouldn't actually work in this method.
+ // defer close(s.startChan) // Don't understand why defer wouldn't actually work in this method (prolly cause the last ActivateAndServe does not actually return?
if err != nil {
close(s.startChan)
return err
@@ -266,8 +266,11 @@ func (s *Server) Stop() (err error) {
// Close the listener now; this stops the server without delay
s.listenerMu.Lock()
- if s.listener != nil {
- err = s.listener.Close()
+ if s.tcp != nil {
+ err = s.tcp.Close()
+ }
+ if s.udp != nil {
+ err = s.udp.Close()
}
for _, s1 := range s.server {
@@ -291,8 +294,21 @@ func (s *Server) WaitUntilStarted() {
func (s *Server) ListenerFd() *os.File {
s.listenerMu.Lock()
defer s.listenerMu.Unlock()
- if s.listener != nil {
- file, _ := s.listener.File()
+ if s.tcp != nil {
+ file, _ := s.tcp.(*net.TCPListener).File()
+ return file
+ }
+ return nil
+}
+
+// PacketConnFd gets a dup'ed file of the packetconn. If there
+// is no underlying file, the return value will be nil. It
+// is the caller's responsibility to close the file.
+func (s *Server) PacketConnFd() *os.File {
+ s.listenerMu.Lock()
+ defer s.listenerMu.Unlock()
+ if s.udp != nil {
+ file, _ := s.udp.(*net.UDPConn).File()
return file
}
return nil